diff options
author | Ievgenii Meshcheriakov <ievgenii.meshcheriakov@qt.io> | 2021-09-21 11:32:01 +0200 |
---|---|---|
committer | Ievgenii Meshcheriakov <ievgenii.meshcheriakov@qt.io> | 2021-10-20 17:04:51 +0200 |
commit | af2f88f5b65d597116140f661030ba1ccf560ab2 (patch) | |
tree | 3c6c3bd30c5e04231b64a039d59600104b4365de | |
parent | bb8d84c358eb4b324258b936bdeb211fdd90d7cd (diff) |
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>
-rw-r--r-- | src/corelib/kernel/qproperty.h | 2 | ||||
-rw-r--r-- | src/corelib/kernel/qproperty_p.h | 36 | ||||
-rw-r--r-- | src/corelib/kernel/qpropertyprivate.h | 4 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp | 57 |
4 files changed, 87 insertions, 12 deletions
diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h index 4358182c25..f653d78fb6 100644 --- a/src/corelib/kernel/qproperty.h +++ b/src/corelib/kernel/qproperty.h @@ -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; diff --git a/src/corelib/kernel/qproperty_p.h b/src/corelib/kernel/qproperty_p.h index 0e01026d54..cc080ff82b 100644 --- a/src/corelib/kernel/qproperty_p.h +++ b/src/corelib/kernel/qproperty_p.h @@ -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__) \ diff --git a/src/corelib/kernel/qpropertyprivate.h b/src/corelib/kernel/qpropertyprivate.h index d3f384748a..fec69f3a72 100644 --- a/src/corelib/kernel/qpropertyprivate.h +++ b/src/corelib/kernel/qpropertyprivate.h @@ -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) diff --git a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp index f386c0473b..ef4c6868cf 100644 --- a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp +++ b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp @@ -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 |