diff options
-rw-r--r-- | src/corelib/animation/qabstractanimation.cpp | 3 | ||||
-rw-r--r-- | src/corelib/itemmodels/qsortfilterproxymodel.cpp | 6 | ||||
-rw-r--r-- | src/corelib/kernel/qproperty.cpp | 39 | ||||
-rw-r--r-- | src/corelib/kernel/qproperty.h | 10 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp | 56 |
5 files changed, 80 insertions, 34 deletions
diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp index 46ed60d6d1..4965ed4fd5 100644 --- a/src/corelib/animation/qabstractanimation.cpp +++ b/src/corelib/animation/qabstractanimation.cpp @@ -1121,7 +1121,7 @@ void QAbstractAnimation::setDirection(Direction direction) return; } - Qt::beginPropertyUpdateGroup(); + const QScopedPropertyUpdateGroup guard; const int oldCurrentLoop = d->currentLoop; if (state() == Stopped) { if (direction == Backward) { @@ -1148,7 +1148,6 @@ void QAbstractAnimation::setDirection(Direction direction) if (d->currentLoop != oldCurrentLoop) d->currentLoop.notify(); d->direction.notify(); - Qt::endPropertyUpdateGroup(); } QBindable<QAbstractAnimation::Direction> QAbstractAnimation::bindableDirection() diff --git a/src/corelib/itemmodels/qsortfilterproxymodel.cpp b/src/corelib/itemmodels/qsortfilterproxymodel.cpp index bf1c2dbd97..db1ed95182 100644 --- a/src/corelib/itemmodels/qsortfilterproxymodel.cpp +++ b/src/corelib/itemmodels/qsortfilterproxymodel.cpp @@ -2582,7 +2582,7 @@ QBindable<QRegularExpression> QSortFilterProxyModel::bindableFilterRegularExpres void QSortFilterProxyModel::setFilterRegularExpression(const QRegularExpression ®ularExpression) { Q_D(QSortFilterProxyModel); - Qt::beginPropertyUpdateGroup(); + const QScopedPropertyUpdateGroup guard; const bool regExpChanged = regularExpression != d->filter_regularexpression.value(); d->filter_regularexpression.removeBindingUnlessInWrapper(); d->filter_casesensitive.removeBindingUnlessInWrapper(); @@ -2601,7 +2601,6 @@ void QSortFilterProxyModel::setFilterRegularExpression(const QRegularExpression d->filter_regularexpression.notify(); if (cs != updatedCs) d->filter_casesensitive.notify(); - Qt::endPropertyUpdateGroup(); } /*! @@ -2677,7 +2676,7 @@ void QSortFilterProxyModel::setFilterCaseSensitivity(Qt::CaseSensitivity cs) if (cs == d->filter_casesensitive) return; - Qt::beginPropertyUpdateGroup(); + const QScopedPropertyUpdateGroup guard; QRegularExpression::PatternOptions options = d->filter_regularexpression.value().patternOptions(); options.setFlag(QRegularExpression::CaseInsensitiveOption, cs == Qt::CaseInsensitive); @@ -2690,7 +2689,6 @@ void QSortFilterProxyModel::setFilterCaseSensitivity(Qt::CaseSensitivity cs) d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows); d->filter_regularexpression.notify(); d->filter_casesensitive.notify(); - Qt::endPropertyUpdateGroup(); } QBindable<Qt::CaseSensitivity> QSortFilterProxyModel::bindableFilterCaseSensitivity() diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp index 8b14390bc6..4708a92b9e 100644 --- a/src/corelib/kernel/qproperty.cpp +++ b/src/corelib/kernel/qproperty.cpp @@ -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>); diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h index 5d8b908f31..dd18d95c50 100644 --- a/src/corelib/kernel/qproperty.h +++ b/src/corelib/kernel/qproperty.h @@ -53,6 +53,16 @@ Q_CORE_EXPORT void beginPropertyUpdateGroup(); Q_CORE_EXPORT void endPropertyUpdateGroup(); } +class [[nodiscard]] QScopedPropertyUpdateGroup +{ + Q_DISABLE_COPY_MOVE(QScopedPropertyUpdateGroup) +public: + QScopedPropertyUpdateGroup() + { Qt::beginPropertyUpdateGroup(); } + ~QScopedPropertyUpdateGroup() noexcept(false) + { Qt::endPropertyUpdateGroup(); } +}; + template <typename T> class QPropertyData : public QUntypedPropertyData { diff --git a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp index c7891291a4..f849cfa770 100644 --- a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp +++ b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp @@ -1782,13 +1782,14 @@ void tst_QProperty::propertyAdaptorBinding() QCOMPARE(object.fooChangedCount, 7); // Check update group - Qt::beginPropertyUpdateGroup(); - source.setValue(23); - source2.setValue(22); - QCOMPARE(object.foo(), 43); - QCOMPARE(dest1.value(), 43); - QCOMPARE(object.fooChangedCount, 7); - Qt::endPropertyUpdateGroup(); + { + const QScopedPropertyUpdateGroup guard; + source.setValue(23); + source2.setValue(22); + QCOMPARE(object.foo(), 43); + QCOMPARE(dest1.value(), 43); + QCOMPARE(object.fooChangedCount, 7); + } QCOMPARE(object.foo(), 45); QCOMPARE(dest1.value(), 45); QCOMPARE(object.fooChangedCount, 8); @@ -2085,27 +2086,29 @@ void tst_QProperty::groupedNotifications() QCOMPARE(nNotifications, 1); expected = 2; - Qt::beginPropertyUpdateGroup(); - a = 1; - QCOMPARE(b.value(), 0); - QCOMPARE(c.value(), 0); - QCOMPARE(d.value(), 0); - QCOMPARE(nNotifications, 1); - Qt::endPropertyUpdateGroup(); + { + const QScopedPropertyUpdateGroup guard; + a = 1; + QCOMPARE(b.value(), 0); + QCOMPARE(c.value(), 0); + QCOMPARE(d.value(), 0); + QCOMPARE(nNotifications, 1); + } QCOMPARE(b.value(), 1); QCOMPARE(c.value(), 1); QCOMPARE(e.value(), 2); QCOMPARE(nNotifications, 2); expected = 7; - Qt::beginPropertyUpdateGroup(); - a = 2; - d = 3; - QCOMPARE(b.value(), 1); - QCOMPARE(c.value(), 1); - QCOMPARE(d.value(), 3); - QCOMPARE(nNotifications, 2); - Qt::endPropertyUpdateGroup(); + { + const QScopedPropertyUpdateGroup guard; + a = 2; + d = 3; + QCOMPARE(b.value(), 1); + QCOMPARE(c.value(), 1); + QCOMPARE(d.value(), 3); + QCOMPARE(nNotifications, 2); + } QCOMPARE(b.value(), 2); QCOMPARE(c.value(), 2); QCOMPARE(e.value(), 7); @@ -2128,10 +2131,11 @@ void tst_QProperty::groupedNotificationConsistency() j = 1; QVERIFY(!areEqual); // value changed runs before j = 1 - Qt::beginPropertyUpdateGroup(); - i = 2; - j = 2; - Qt::endPropertyUpdateGroup(); + { + const QScopedPropertyUpdateGroup guard; + i = 2; + j = 2; + } QVERIFY(areEqual); // value changed runs after everything has been evaluated } |