QObjectCompatProperty: Add support for custom getters

Add additional template argument to QObjectCompatProperty to specify
a custom getter. This may be useful for classes like
QAbstractProxyModelPrivate the need to customize property getters.

Task-number: QTBUG-89655
Change-Id: I34fe4bdebbbf1446aff60bd20a946454607f52d5
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Ievgenii Meshcheriakov 2021-09-21 11:32:01 +02:00
parent bb8d84c358
commit af2f88f5b6
4 changed files with 87 additions and 12 deletions

View File

@ -956,7 +956,7 @@ class Q_CORE_EXPORT QBindingStorage
mutable QBindingStorageData *d = nullptr;
QBindingStatus *bindingStatus = nullptr;
template<typename Class, typename T, auto Offset, auto Setter, auto Signal>
template<typename Class, typename T, auto Offset, auto Setter, auto Signal, auto Getter>
friend class QObjectCompatProperty;
friend class QObjectPrivate;
friend class QtPrivate::QPropertyBindingData;

View File

@ -447,13 +447,14 @@ namespace QtPrivate {
void Q_CORE_EXPORT initBindingStatusThreadId();
}
template<typename Class, typename T, auto Offset, auto Setter, auto Signal=nullptr>
template<typename Class, typename T, auto Offset, auto Setter, auto Signal = nullptr,
auto Getter = nullptr>
class QObjectCompatProperty : public QPropertyData<T>
{
template<typename Property, typename>
friend class QtPrivate::QBindableInterfaceForProperty;
using ThisType = QObjectCompatProperty<Class, T, Offset, Setter, Signal>;
using ThisType = QObjectCompatProperty<Class, T, Offset, Setter, Signal, Getter>;
using SignalTakesValue = std::is_invocable<decltype(Signal), Class, T>;
Class *owner()
{
@ -486,6 +487,14 @@ class QObjectCompatProperty : public QPropertyData<T>
&& QtPrivate::isPropertyInBindingWrapper(this);
}
inline static T getPropertyValue(const QUntypedPropertyData *d) {
auto prop = static_cast<const ThisType *>(d);
if constexpr (std::is_null_pointer_v<decltype(Getter)>)
return prop->value();
else
return (prop->owner()->*Getter)();
}
public:
using value_type = typename QPropertyData<T>::value_type;
using parameter_type = typename QPropertyData<T>::parameter_type;
@ -596,7 +605,7 @@ public:
}
if constexpr (!std::is_null_pointer_v<decltype(Signal)>) {
if constexpr (SignalTakesValue::value)
(owner()->*Signal)(value());
(owner()->*Signal)(getPropertyValue(this));
else
(owner()->*Signal)();
}
@ -650,15 +659,16 @@ private:
};
namespace QtPrivate {
template<typename Class, typename Ty, auto Offset, auto Setter, auto Signal>
class QBindableInterfaceForProperty<QObjectCompatProperty<Class, Ty, Offset, Setter, Signal>, std::void_t<Class>>
template<typename Class, typename Ty, auto Offset, auto Setter, auto Signal, auto Getter>
class QBindableInterfaceForProperty<
QObjectCompatProperty<Class, Ty, Offset, Setter, Signal, Getter>, std::void_t<Class>>
{
using Property = QObjectCompatProperty<Class, Ty, Offset, Setter, Signal>;
using Property = QObjectCompatProperty<Class, Ty, Offset, Setter, Signal, Getter>;
using T = typename Property::value_type;
public:
static constexpr QBindableInterface iface = {
[](const QUntypedPropertyData *d, void *value) -> void
{ *static_cast<T*>(value) = static_cast<const Property *>(d)->value(); },
{ *static_cast<T*>(value) = Property::getPropertyValue(d); },
[](QUntypedPropertyData *d, const void *value) -> void
{
(static_cast<Property *>(d)->owner()->*Setter)(*static_cast<const T*>(value));
@ -668,7 +678,7 @@ public:
[](QUntypedPropertyData *d, const QUntypedPropertyBinding &binding) -> QUntypedPropertyBinding
{ return static_cast<Property *>(d)->setBinding(static_cast<const QPropertyBinding<T> &>(binding)); },
[](const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding
{ return Qt::makePropertyBinding([d]() -> T { return static_cast<const Property *>(d)->value(); }, location); },
{ return Qt::makePropertyBinding([d]() -> T { return Property::getPropertyValue(d); }, location); },
[](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void
{ observer->setSource(static_cast<const Property *>(d)->bindingData()); },
[]() { return QMetaType::fromType<T>(); }
@ -717,6 +727,16 @@ public:
QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter, \
signal>(value);
#define QT_OBJECT_COMPAT_PROPERTY_WITH_ARGS_7(Class, Type, name, setter, signal, getter, value) \
static constexpr size_t _qt_property_##name##_offset() { \
QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
return offsetof(Class, name); \
QT_WARNING_POP \
} \
QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter, signal, getter>\
name = QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter, \
signal, getter>(value);
#define Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(...) \
QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
QT_OVERLOADED_MACRO(QT_OBJECT_COMPAT_PROPERTY_WITH_ARGS, __VA_ARGS__) \

View File

@ -61,7 +61,7 @@ QT_BEGIN_NAMESPACE
class QBindingStorage;
template<typename Class, typename T, auto Offset, auto Setter, auto Signal>
template<typename Class, typename T, auto Offset, auto Setter, auto Signal, auto Getter>
class QObjectCompatProperty;
namespace QtPrivate {
@ -263,7 +263,7 @@ class Q_CORE_EXPORT QPropertyBindingData
friend class QT_PREPEND_NAMESPACE(QQmlPropertyBinding);
friend struct QT_PREPEND_NAMESPACE(QPropertyDelayedNotifications);
template<typename Class, typename T, auto Offset, auto Setter, auto Signal>
template<typename Class, typename T, auto Offset, auto Setter, auto Signal, auto Getter>
friend class QT_PREPEND_NAMESPACE(QObjectCompatProperty);
Q_DISABLE_COPY(QPropertyBindingData)

View File

@ -1494,6 +1494,7 @@ class CompatPropertyTester : public QObject
Q_PROPERTY(int prop1 READ prop1 WRITE setProp1 BINDABLE bindableProp1)
Q_PROPERTY(int prop2 READ prop2 WRITE setProp2 NOTIFY prop2Changed BINDABLE bindableProp2)
Q_PROPERTY(int prop3 READ prop3 WRITE setProp3 NOTIFY prop3Changed BINDABLE bindableProp3)
Q_PROPERTY(int prop4 READ prop4 WRITE setProp4 NOTIFY prop4Changed BINDABLE bindableProp4)
public:
CompatPropertyTester(QObject *parent = nullptr) : QObject(parent) { }
@ -1521,9 +1522,25 @@ public:
}
QBindable<int> bindableProp3() { return QBindable<int>(&prop3Data); }
int prop4() const
{
auto val = prop4Data.value();
return val == 0 ? 42 : val;
}
void setProp4(int i)
{
if (i == prop4Data)
return;
prop4Data.setValue(i);
prop4Data.notify();
}
QBindable<int> bindableProp4() { return QBindable<int>(&prop4Data); }
signals:
void prop2Changed(int value);
void prop3Changed();
void prop4Changed(int value);
private:
Q_OBJECT_COMPAT_PROPERTY(CompatPropertyTester, int, prop1Data, &CompatPropertyTester::setProp1)
@ -1532,6 +1549,10 @@ private:
Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(CompatPropertyTester, int, prop3Data,
&CompatPropertyTester::setProp3,
&CompatPropertyTester::prop3Changed, 1)
Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(CompatPropertyTester, int, prop4Data,
&CompatPropertyTester::setProp4,
&CompatPropertyTester::prop4Changed,
&CompatPropertyTester::prop4, 0)
};
void tst_QProperty::compatPropertyNoDobuleNotification()
@ -1559,7 +1580,7 @@ void tst_QProperty::compatPropertySignals()
QCOMPARE(prop2Observer.value(), 10);
QCOMPARE(prop2Spy.count(), 1);
const QList<QVariant> arguments = prop2Spy.takeFirst();
QList<QVariant> arguments = prop2Spy.takeFirst();
QCOMPARE(arguments.size(), 1);
QCOMPARE(arguments.at(0).metaType().id(), QMetaType::Int);
QCOMPARE(arguments.at(0).toInt(), 10);
@ -1575,6 +1596,40 @@ void tst_QProperty::compatPropertySignals()
QCOMPARE(prop3Observer.value(), 5);
QCOMPARE(prop3Spy.count(), 1);
// Compat property with signal, default value, and custom setter. Signal has parameter.
QProperty<int> prop4Observer;
prop4Observer.setBinding(tester.bindableProp4().makeBinding());
QCOMPARE(prop4Observer.value(), 42);
QSignalSpy prop4Spy(&tester, &CompatPropertyTester::prop4Changed);
tester.setProp4(10);
QCOMPARE(prop4Observer.value(), 10);
QCOMPARE(prop4Spy.count(), 1);
arguments = prop4Spy.takeFirst();
QCOMPARE(arguments.size(), 1);
QCOMPARE(arguments.at(0).metaType().id(), QMetaType::Int);
QCOMPARE(arguments.at(0).toInt(), 10);
tester.setProp4(42);
QCOMPARE(prop4Observer.value(), 42);
QCOMPARE(prop4Spy.count(), 1);
arguments = prop4Spy.takeFirst();
QCOMPARE(arguments.size(), 1);
QCOMPARE(arguments.at(0).metaType().id(), QMetaType::Int);
QCOMPARE(arguments.at(0).toInt(), 42);
tester.setProp4(0);
QCOMPARE(prop4Observer.value(), 42);
QCOMPARE(prop4Spy.count(), 1);
arguments = prop4Spy.takeFirst();
QCOMPARE(arguments.size(), 1);
QCOMPARE(arguments.at(0).metaType().id(), QMetaType::Int);
QCOMPARE(arguments.at(0).toInt(), 42);
}
class FakeDependencyCreator : public QObject