summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIevgenii Meshcheriakov <ievgenii.meshcheriakov@qt.io>2021-09-21 11:32:01 +0200
committerIevgenii Meshcheriakov <ievgenii.meshcheriakov@qt.io>2021-10-20 17:04:51 +0200
commitaf2f88f5b65d597116140f661030ba1ccf560ab2 (patch)
tree3c6c3bd30c5e04231b64a039d59600104b4365de
parentbb8d84c358eb4b324258b936bdeb211fdd90d7cd (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.h2
-rw-r--r--src/corelib/kernel/qproperty_p.h36
-rw-r--r--src/corelib/kernel/qpropertyprivate.h4
-rw-r--r--tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp57
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