From 240505457bb1e8280682c7274493a7a6f61a1045 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Tue, 15 Dec 2020 11:09:16 +0100 Subject: [PATCH] QBindable: Improve read-only support If we create a QBindable from a const property, we should obtain a read-only interface. Besides implementing this feature, this patch adds a isReadOnly method to Q(Untyped)Bindable which can be used to check whether one can modify the property via the bindable interface. Task-number: QTBUG-89505 Task-number: QTBUG-89469 Change-Id: Ic36949a5b84c5119e0060ed0a1cf4ac94a66f341 Reviewed-by: Ulf Hermann --- src/corelib/kernel/qproperty.h | 25 ++++++++++++++++++- .../kernel/qproperty/tst_qproperty.cpp | 8 ++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h index 8fda7715c2..f853eb8634 100644 --- a/src/corelib/kernel/qproperty.h +++ b/src/corelib/kernel/qproperty.h @@ -494,7 +494,7 @@ class QBindableInterfaceForProperty { using T = typename Property::value_type; public: - // interface for read-only properties. Those do not have a binding()/setBinding() method, but one can + // interface for computed properties. Those do not have a binding()/setBinding() method, but one can // install observers on them. static constexpr QBindableInterface iface = { [](const QUntypedPropertyData *d, void *value) -> void @@ -510,6 +510,28 @@ public: }; }; +template +class QBindableInterfaceForProperty().binding())>> +{ + using T = typename Property::value_type; +public: + // A bindable created from a const property results in a read-only interface, too. + static constexpr QBindableInterface iface = { + + [](const QUntypedPropertyData *d, void *value) -> void + { *static_cast(value) = static_cast(d)->value(); }, + /*setter=*/nullptr, + [](const QUntypedPropertyData *d) -> QUntypedPropertyBinding + { return static_cast(d)->binding(); }, + /*setBinding=*/nullptr, + [](const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding + { return Qt::makePropertyBinding([d]() -> T { return static_cast(d)->value(); }, location); }, + [](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void + { observer->setSource(static_cast(d)->bindingData()); }, + []() { return QMetaType::fromType(); } + }; +}; + template class QBindableInterfaceForProperty().binding())>> { @@ -553,6 +575,7 @@ public: bool isValid() const { return data != nullptr; } bool isBindable() const { return iface && iface->getBinding; } + bool isReadOnly() const { return !(iface && iface->setBinding && iface->setObserver); } QUntypedPropertyBinding makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) { diff --git a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp index 4559587afd..7de6458406 100644 --- a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp +++ b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp @@ -1048,6 +1048,7 @@ public: } QBindable bindableFoo() { return QBindable(&fooData); } + const QBindable bindableFoo() const { return QBindable(&fooData); } QBindable bindableBar() { return QBindable(&barData); } QBindable bindableRead() { return QBindable(&readData); } QBindable bindableComputed() { return QBindable(&computedData); } @@ -1068,7 +1069,14 @@ public: void tst_QProperty::testNewStuff() { + MyQObject testReadOnly; + testReadOnly.bindableFoo().setBinding([](){return 42;}); + auto bindable = const_cast(testReadOnly).bindableFoo(); + QVERIFY(bindable.hasBinding()); + QVERIFY(bindable.isReadOnly()); + MyQObject object; + QVERIFY(!object.bindableFoo().isReadOnly()); QObject::connect(&object, &MyQObject::fooChanged, &object, &MyQObject::fooHasChanged); QObject::connect(&object, &MyQObject::barChanged, &object, &MyQObject::barHasChanged);