Add support for computed properties
Add a QObjectComputedProperty. This class doesn't store the data itself, instead relies on a getter method to compute it's value. As the property is read-only, one can not bind to it, but it can be used in other property bindings. Change-Id: I0f6bffdd9f80f1d0829826f93a47257f2b3127af Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
parent
1e1b888092
commit
918c61f275
@ -929,6 +929,82 @@ private:
|
||||
} \
|
||||
QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, __VA_ARGS__> name;
|
||||
|
||||
template<typename Class, typename T, auto Offset, auto Getter>
|
||||
class QObjectComputedProperty : public QUntypedPropertyData
|
||||
{
|
||||
Class *owner()
|
||||
{
|
||||
char *that = reinterpret_cast<char *>(this);
|
||||
return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
|
||||
}
|
||||
const Class *owner() const
|
||||
{
|
||||
char *that = const_cast<char *>(reinterpret_cast<const char *>(this));
|
||||
return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
|
||||
}
|
||||
public:
|
||||
using value_type = T;
|
||||
using parameter_type = T;
|
||||
|
||||
QObjectComputedProperty() = default;
|
||||
|
||||
parameter_type value() const {
|
||||
qGetBindingStorage(owner())->maybeUpdateBindingAndRegister(this);
|
||||
return (owner()->*Getter)();
|
||||
}
|
||||
|
||||
std::conditional_t<QTypeTraits::is_dereferenceable_v<T>, parameter_type, void>
|
||||
operator->() const
|
||||
{
|
||||
if constexpr (QTypeTraits::is_dereferenceable_v<T>)
|
||||
return value();
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
parameter_type operator*() const
|
||||
{
|
||||
return value();
|
||||
}
|
||||
|
||||
operator parameter_type() const
|
||||
{
|
||||
return value();
|
||||
}
|
||||
|
||||
constexpr bool hasBinding() const { return false; }
|
||||
|
||||
template<typename Functor>
|
||||
QPropertyChangeHandler<Functor> onValueChanged(Functor f)
|
||||
{
|
||||
static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
|
||||
return QPropertyChangeHandler<Functor>(*this, f);
|
||||
}
|
||||
|
||||
template<typename Functor>
|
||||
QPropertyChangeHandler<Functor> subscribe(Functor f)
|
||||
{
|
||||
static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
|
||||
f();
|
||||
return onValueChanged(f);
|
||||
}
|
||||
|
||||
QtPrivate::QPropertyBindingData &bindingData() const
|
||||
{
|
||||
auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
|
||||
return *storage->bindingData(const_cast<QObjectComputedProperty *>(this), true);
|
||||
}
|
||||
private:
|
||||
};
|
||||
|
||||
#define Q_OBJECT_COMPUTED_PROPERTY(Class, Type, name, ...) \
|
||||
static constexpr size_t _qt_property_##name##_offset() { \
|
||||
QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
|
||||
return offsetof(Class, name); \
|
||||
QT_WARNING_POP \
|
||||
} \
|
||||
QObjectComputedProperty<Class, Type, Class::_qt_property_##name##_offset, __VA_ARGS__> name;
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QPROPERTY_H
|
||||
|
@ -968,6 +968,7 @@ class MyQObject : public QObject
|
||||
Q_PROPERTY(int foo READ foo WRITE setFoo NOTIFY fooChanged) // Use Q_BINDABLE_PROPERTY and generate iface API
|
||||
Q_PROPERTY(int bar READ bar WRITE setBar NOTIFY barChanged)
|
||||
Q_PROPERTY(int read READ read NOTIFY readChanged)
|
||||
Q_PROPERTY(int computed READ computed STORED false)
|
||||
|
||||
signals:
|
||||
void fooChanged();
|
||||
@ -985,10 +986,12 @@ public:
|
||||
int bar() const { return barData.value(); }
|
||||
void setBar(int i) { barData.setValue(i); }
|
||||
int read() const { return readData.value(); }
|
||||
int computed() const { return readData.value(); }
|
||||
|
||||
QBindable<int> bindableFoo() { 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); }
|
||||
|
||||
public:
|
||||
int fooChangedCount = 0;
|
||||
@ -998,6 +1001,7 @@ public:
|
||||
Q_OBJECT_BINDABLE_PROPERTY(MyQObject, int, fooData, &MyQObject::fooChanged);
|
||||
Q_OBJECT_BINDABLE_PROPERTY(MyQObject, int, barData, &MyQObject::barChanged);
|
||||
Q_OBJECT_BINDABLE_PROPERTY(MyQObject, int, readData, &MyQObject::readChanged);
|
||||
Q_OBJECT_COMPUTED_PROPERTY(MyQObject, int, computedData, &MyQObject::computed);
|
||||
};
|
||||
|
||||
void tst_QProperty::testNewStuff()
|
||||
@ -1049,6 +1053,16 @@ void tst_QProperty::testNewStuff()
|
||||
QCOMPARE(object.foo(), 0);
|
||||
object.readData.setValue(10);
|
||||
QCOMPARE(object.foo(), 10);
|
||||
|
||||
QCOMPARE(object.computed(), 10);
|
||||
object.readData.setValue(42);
|
||||
QCOMPARE(object.computed(), 42);
|
||||
|
||||
object.bindableBar().setBinding(object.bindableComputed().makeBinding());
|
||||
QCOMPARE(object.computed(), 42);
|
||||
object.readData.setValue(111);
|
||||
QCOMPARE(object.computed(), 111);
|
||||
|
||||
}
|
||||
|
||||
void tst_QProperty::qobjectObservers()
|
||||
|
Loading…
Reference in New Issue
Block a user