summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/animation/qabstractanimation.cpp3
-rw-r--r--src/corelib/itemmodels/qsortfilterproxymodel.cpp6
-rw-r--r--src/corelib/kernel/qproperty.cpp39
-rw-r--r--src/corelib/kernel/qproperty.h10
-rw-r--r--tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp56
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 &regularExpression)
{
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
}