diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp index 75110fa031..ba91db3b5d 100644 --- a/src/corelib/kernel/qproperty.cpp +++ b/src/corelib/kernel/qproperty.cpp @@ -610,6 +610,19 @@ QPropertyBindingSourceLocation QPropertyBindingError::location() const this property is read. */ +/*! + \fn template QPropertyBinding bool QProperty::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 QPropertyBinding QProperty::binding() const diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h index 60e4193827..7fd8c2e17e 100644 --- a/src/corelib/kernel/qproperty.h +++ b/src/corelib/kernel/qproperty.h @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -120,10 +121,10 @@ public: using BindingEvaluationResult = std::variant; // returns true if value changed, false if the binding evaluation lead to the same value as the property // already has. - using BindingEvaluationFunction = std::function; + using BindingEvaluationFunction = std::function; QUntypedPropertyBinding(); - QUntypedPropertyBinding(BindingEvaluationFunction function, const QPropertyBindingSourceLocation &location); + QUntypedPropertyBinding(const QMetaType &metaType, BindingEvaluationFunction function, const QPropertyBindingSourceLocation &location); QUntypedPropertyBinding(QUntypedPropertyBinding &&other); QUntypedPropertyBinding(const QUntypedPropertyBinding &other); QUntypedPropertyBinding &operator=(const QUntypedPropertyBinding &other); @@ -134,6 +135,8 @@ public: QPropertyBindingError error() const; + QMetaType valueMetaType() const; + private: explicit QUntypedPropertyBinding(const QPropertyBindingPrivatePtr &priv); friend class QtPrivate::QPropertyBase; @@ -149,15 +152,18 @@ class QPropertyBinding : public QUntypedPropertyBinding struct BindingAdaptor { Functor impl; - QUntypedPropertyBinding::BindingEvaluationResult operator()(int /*version*/, void *propertyStoragePtr) + QUntypedPropertyBinding::BindingEvaluationResult operator()(const QMetaType &/*metaType*/, void *dataPtr) { std::variant result(impl()); if (auto errorPtr = std::get_if(&result)) return *errorPtr; if (auto valuePtr = std::get_if(&result)) { - auto storagePtr = reinterpret_cast*>(propertyStoragePtr); - return storagePtr->setValueAndReturnTrueIfChanged(std::move(*valuePtr)); + PropertyType *propertyPtr = reinterpret_cast(dataPtr); + if (*propertyPtr == *valuePtr) + return false; + *propertyPtr = std::move(*valuePtr); + return true; } return false; @@ -169,7 +175,7 @@ public: template QPropertyBinding(Functor &&f, const QPropertyBindingSourceLocation &location) - : QUntypedPropertyBinding(BindingAdaptor{std::forward(f)}, location) + : QUntypedPropertyBinding(QMetaType::fromType(), BindingAdaptor{std::forward(f)}, location) {} QPropertyBinding(const QProperty &property) @@ -212,6 +218,8 @@ template class QProperty { public: + using value_type = T; + QProperty() = default; explicit QProperty(const T &initialValue) : d(initialValue) {} explicit QProperty(T &&initialValue) : d(std::move(initialValue)) {} @@ -299,6 +307,15 @@ public: return oldBinding; } + bool setBinding(const QUntypedPropertyBinding &newBinding) + { + if (newBinding.valueMetaType().id() != qMetaTypeId()) + return false; + d.priv.setBinding(newBinding, &d); + notify(); + return true; + } + #ifndef Q_CLANG_QDOC template QPropertyBinding setBinding(Functor f, diff --git a/src/corelib/kernel/qpropertybinding.cpp b/src/corelib/kernel/qpropertybinding.cpp index 8acaaeee72..fb372fcdfc 100644 --- a/src/corelib/kernel/qpropertybinding.cpp +++ b/src/corelib/kernel/qpropertybinding.cpp @@ -79,22 +79,37 @@ bool QPropertyBindingPrivate::evaluateIfDirtyAndReturnTrueIfValueChanged() QScopedValueRollback updateGuard(updating, true); BindingEvaluationState evaluationFrame(this); + + QUntypedPropertyBinding::BindingEvaluationResult result; bool changed = false; - auto result = evaluationFunction(/*version*/1, propertyDataPtr); - if (auto changedPtr = std::get_if(&result)) - changed = *changedPtr; - else if (auto errorPtr = std::get_if(&result)) + if (metaType.id() == QMetaType::Bool) { + auto propertyPtr = reinterpret_cast(propertyDataPtr); + bool temp = propertyPtr->extraBit(); + result = evaluationFunction(metaType, &temp); + if (auto changedPtr = std::get_if(&result)) { + changed = *changedPtr; + if (changed) + propertyPtr->setExtraBit(temp); + } + } else { + result = evaluationFunction(metaType, propertyDataPtr); + if (auto changedPtr = std::get_if(&result)) + changed = *changedPtr; + } + + if (auto errorPtr = std::get_if(&result)) error = std::move(*errorPtr); + dirty = false; return changed; } QUntypedPropertyBinding::QUntypedPropertyBinding() = default; -QUntypedPropertyBinding::QUntypedPropertyBinding(QUntypedPropertyBinding::BindingEvaluationFunction function, +QUntypedPropertyBinding::QUntypedPropertyBinding(const QMetaType &metaType, QUntypedPropertyBinding::BindingEvaluationFunction function, const QPropertyBindingSourceLocation &location) { - d = new QPropertyBindingPrivate(std::move(function), std::move(location)); + d = new QPropertyBindingPrivate(metaType, std::move(function), std::move(location)); } QUntypedPropertyBinding::QUntypedPropertyBinding(QUntypedPropertyBinding &&other) @@ -140,4 +155,11 @@ QPropertyBindingError QUntypedPropertyBinding::error() const return d->error; } +QMetaType QUntypedPropertyBinding::valueMetaType() const +{ + if (!d) + return QMetaType(); + return d->metaType; +} + QT_END_NAMESPACE diff --git a/src/corelib/kernel/qpropertybinding_p.h b/src/corelib/kernel/qpropertybinding_p.h index 12913b3088..92638c92cc 100644 --- a/src/corelib/kernel/qpropertybinding_p.h +++ b/src/corelib/kernel/qpropertybinding_p.h @@ -74,13 +74,16 @@ struct QPropertyBindingPrivate : public QSharedData QPropertyBindingSourceLocation location; QPropertyBindingError error; + QMetaType metaType; + bool dirty = false; bool updating = false; - QPropertyBindingPrivate(QUntypedPropertyBinding::BindingEvaluationFunction evaluationFunction, + QPropertyBindingPrivate(const QMetaType &metaType, QUntypedPropertyBinding::BindingEvaluationFunction evaluationFunction, const QPropertyBindingSourceLocation &location) : evaluationFunction(std::move(evaluationFunction)) , location(location) + , metaType(metaType) {} virtual ~QPropertyBindingPrivate(); diff --git a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp index 7f64072df6..942865f3a3 100644 --- a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp +++ b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp @@ -65,6 +65,8 @@ private slots: void changePropertyFromWithinChangeHandlerThroughDependency(); void changePropertyFromWithinChangeHandler2(); void settingPropertyValueDoesRemoveBinding(); + void genericPropertyBinding(); + void genericPropertyBindingBool(); }; void tst_QProperty::functorBinding() @@ -610,6 +612,54 @@ void tst_QProperty::settingPropertyValueDoesRemoveBinding() QVERIFY(property.binding().isNull()); } +void tst_QProperty::genericPropertyBinding() +{ + QProperty property; + + { + QUntypedPropertyBinding doubleBinding(QMetaType::fromType(), + [](const QMetaType &, void *) -> bool { + Q_ASSERT(false); + return false; + }, QPropertyBindingSourceLocation()); + QVERIFY(!property.setBinding(doubleBinding)); + } + + QUntypedPropertyBinding intBinding(QMetaType::fromType(), + [](const QMetaType &metaType, void *dataPtr) -> bool { + Q_ASSERT(metaType.id() == qMetaTypeId()); + + int *intPtr = reinterpret_cast(dataPtr); + if (*intPtr == 100) + return false; + *intPtr = 100; + return true; + }, QPropertyBindingSourceLocation()); + + QVERIFY(property.setBinding(intBinding)); + + QCOMPARE(property.value(), 100); +} + +void tst_QProperty::genericPropertyBindingBool() +{ + QProperty property; + + QVERIFY(!property.value()); + + QUntypedPropertyBinding boolBinding(QMetaType::fromType(), + [](const QMetaType &, void *dataPtr) -> bool { + auto boolPtr = reinterpret_cast(dataPtr); + if (*boolPtr) + return false; + *boolPtr = true; + return true; + }, QPropertyBindingSourceLocation()); + QVERIFY(property.setBinding(boolBinding)); + + QVERIFY(property.value()); +} + QTEST_MAIN(tst_QProperty); #include "tst_qproperty.moc"