Enable generic property bindings to QProperty<T>
A generic binding allows implementing the binding function in a way that enables the QML engine to run binding scripts and convert the V4::Value into a QVariant and then assign the value to the property with the help of QMetaType::construct. Change-Id: Id4807be92eee7e3501908e6c5e4c861cfcb7772a Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
parent
3c2aa878dc
commit
1fcce51053
@ -610,6 +610,19 @@ QPropertyBindingSourceLocation QPropertyBindingError::location() const
|
||||
this property is read.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename T> QPropertyBinding<T> bool QProperty<T>::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 <typename T> QPropertyBinding<T> QProperty<T>::binding() const
|
||||
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QtCore/QSharedDataPointer>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/qmetatype.h>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
@ -120,10 +121,10 @@ public:
|
||||
using BindingEvaluationResult = std::variant<bool, QPropertyBindingError>;
|
||||
// returns true if value changed, false if the binding evaluation lead to the same value as the property
|
||||
// already has.
|
||||
using BindingEvaluationFunction = std::function<BindingEvaluationResult(int version, void *propertyStoragePtr)>;
|
||||
using BindingEvaluationFunction = std::function<BindingEvaluationResult(const QMetaType &metaType, void *dataPtr)>;
|
||||
|
||||
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<PropertyType, QPropertyBindingError> result(impl());
|
||||
if (auto errorPtr = std::get_if<QPropertyBindingError>(&result))
|
||||
return *errorPtr;
|
||||
|
||||
if (auto valuePtr = std::get_if<PropertyType>(&result)) {
|
||||
auto storagePtr = reinterpret_cast<QtPrivate::QPropertyValueStorage<PropertyType>*>(propertyStoragePtr);
|
||||
return storagePtr->setValueAndReturnTrueIfChanged(std::move(*valuePtr));
|
||||
PropertyType *propertyPtr = reinterpret_cast<PropertyType *>(dataPtr);
|
||||
if (*propertyPtr == *valuePtr)
|
||||
return false;
|
||||
*propertyPtr = std::move(*valuePtr);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -169,7 +175,7 @@ public:
|
||||
|
||||
template<typename Functor>
|
||||
QPropertyBinding(Functor &&f, const QPropertyBindingSourceLocation &location)
|
||||
: QUntypedPropertyBinding(BindingAdaptor<Functor>{std::forward<Functor>(f)}, location)
|
||||
: QUntypedPropertyBinding(QMetaType::fromType<PropertyType>(), BindingAdaptor<Functor>{std::forward<Functor>(f)}, location)
|
||||
{}
|
||||
|
||||
QPropertyBinding(const QProperty<PropertyType> &property)
|
||||
@ -212,6 +218,8 @@ template <typename T>
|
||||
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<T>())
|
||||
return false;
|
||||
d.priv.setBinding(newBinding, &d);
|
||||
notify();
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef Q_CLANG_QDOC
|
||||
template <typename Functor>
|
||||
QPropertyBinding<T> setBinding(Functor f,
|
||||
|
@ -79,22 +79,37 @@ bool QPropertyBindingPrivate::evaluateIfDirtyAndReturnTrueIfValueChanged()
|
||||
QScopedValueRollback<bool> updateGuard(updating, true);
|
||||
|
||||
BindingEvaluationState evaluationFrame(this);
|
||||
|
||||
QUntypedPropertyBinding::BindingEvaluationResult result;
|
||||
bool changed = false;
|
||||
auto result = evaluationFunction(/*version*/1, propertyDataPtr);
|
||||
if (auto changedPtr = std::get_if<bool>(&result))
|
||||
changed = *changedPtr;
|
||||
else if (auto errorPtr = std::get_if<QPropertyBindingError>(&result))
|
||||
if (metaType.id() == QMetaType::Bool) {
|
||||
auto propertyPtr = reinterpret_cast<QPropertyBase *>(propertyDataPtr);
|
||||
bool temp = propertyPtr->extraBit();
|
||||
result = evaluationFunction(metaType, &temp);
|
||||
if (auto changedPtr = std::get_if<bool>(&result)) {
|
||||
changed = *changedPtr;
|
||||
if (changed)
|
||||
propertyPtr->setExtraBit(temp);
|
||||
}
|
||||
} else {
|
||||
result = evaluationFunction(metaType, propertyDataPtr);
|
||||
if (auto changedPtr = std::get_if<bool>(&result))
|
||||
changed = *changedPtr;
|
||||
}
|
||||
|
||||
if (auto errorPtr = std::get_if<QPropertyBindingError>(&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
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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<int> property;
|
||||
|
||||
{
|
||||
QUntypedPropertyBinding doubleBinding(QMetaType::fromType<double>(),
|
||||
[](const QMetaType &, void *) -> bool {
|
||||
Q_ASSERT(false);
|
||||
return false;
|
||||
}, QPropertyBindingSourceLocation());
|
||||
QVERIFY(!property.setBinding(doubleBinding));
|
||||
}
|
||||
|
||||
QUntypedPropertyBinding intBinding(QMetaType::fromType<int>(),
|
||||
[](const QMetaType &metaType, void *dataPtr) -> bool {
|
||||
Q_ASSERT(metaType.id() == qMetaTypeId<int>());
|
||||
|
||||
int *intPtr = reinterpret_cast<int*>(dataPtr);
|
||||
if (*intPtr == 100)
|
||||
return false;
|
||||
*intPtr = 100;
|
||||
return true;
|
||||
}, QPropertyBindingSourceLocation());
|
||||
|
||||
QVERIFY(property.setBinding(intBinding));
|
||||
|
||||
QCOMPARE(property.value(), 100);
|
||||
}
|
||||
|
||||
void tst_QProperty::genericPropertyBindingBool()
|
||||
{
|
||||
QProperty<bool> property;
|
||||
|
||||
QVERIFY(!property.value());
|
||||
|
||||
QUntypedPropertyBinding boolBinding(QMetaType::fromType<bool>(),
|
||||
[](const QMetaType &, void *dataPtr) -> bool {
|
||||
auto boolPtr = reinterpret_cast<bool *>(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"
|
||||
|
Loading…
Reference in New Issue
Block a user