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 <ulf.hermann@qt.io>
This commit is contained in:
Fabian Kosmale 2020-12-15 11:09:16 +01:00 committed by Andreas Buhr
parent 9f894788dd
commit 240505457b
2 changed files with 32 additions and 1 deletions

View File

@ -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<typename Property>
class QBindableInterfaceForProperty<const Property, std::void_t<decltype(std::declval<Property>().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<T*>(value) = static_cast<const Property *>(d)->value(); },
/*setter=*/nullptr,
[](const QUntypedPropertyData *d) -> QUntypedPropertyBinding
{ return static_cast<const Property *>(d)->binding(); },
/*setBinding=*/nullptr,
[](const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding
{ return Qt::makePropertyBinding([d]() -> T { return static_cast<const Property *>(d)->value(); }, location); },
[](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void
{ observer->setSource(static_cast<const Property *>(d)->bindingData()); },
[]() { return QMetaType::fromType<T>(); }
};
};
template<typename Property>
class QBindableInterfaceForProperty<Property, std::void_t<decltype(std::declval<Property>().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)
{

View File

@ -1048,6 +1048,7 @@ public:
}
QBindable<int> bindableFoo() { return QBindable<int>(&fooData); }
const QBindable<int> bindableFoo() const { return QBindable<int>(&fooData); }
QBindable<int> bindableBar() { return QBindable<int>(&barData); }
QBindable<int> bindableRead() { return QBindable<int>(&readData); }
QBindable<int> bindableComputed() { return QBindable<int>(&computedData); }
@ -1068,7 +1069,14 @@ public:
void tst_QProperty::testNewStuff()
{
MyQObject testReadOnly;
testReadOnly.bindableFoo().setBinding([](){return 42;});
auto bindable = const_cast<const MyQObject&>(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);