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;
|
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
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // QPROPERTY_H
|
#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 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 bar READ bar WRITE setBar NOTIFY barChanged)
|
||||||
Q_PROPERTY(int read READ read NOTIFY readChanged)
|
Q_PROPERTY(int read READ read NOTIFY readChanged)
|
||||||
|
Q_PROPERTY(int computed READ computed STORED false)
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void fooChanged();
|
void fooChanged();
|
||||||
@ -985,10 +986,12 @@ public:
|
|||||||
int bar() const { return barData.value(); }
|
int bar() const { return barData.value(); }
|
||||||
void setBar(int i) { barData.setValue(i); }
|
void setBar(int i) { barData.setValue(i); }
|
||||||
int read() const { return readData.value(); }
|
int read() const { return readData.value(); }
|
||||||
|
int computed() const { return readData.value(); }
|
||||||
|
|
||||||
QBindable<int> bindableFoo() { return QBindable<int>(&fooData); }
|
QBindable<int> bindableFoo() { return QBindable<int>(&fooData); }
|
||||||
QBindable<int> bindableBar() { return QBindable<int>(&barData); }
|
QBindable<int> bindableBar() { return QBindable<int>(&barData); }
|
||||||
QBindable<int> bindableRead() { return QBindable<int>(&readData); }
|
QBindable<int> bindableRead() { return QBindable<int>(&readData); }
|
||||||
|
QBindable<int> bindableComputed() { return QBindable<int>(&computedData); }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int fooChangedCount = 0;
|
int fooChangedCount = 0;
|
||||||
@ -998,6 +1001,7 @@ public:
|
|||||||
Q_OBJECT_BINDABLE_PROPERTY(MyQObject, int, fooData, &MyQObject::fooChanged);
|
Q_OBJECT_BINDABLE_PROPERTY(MyQObject, int, fooData, &MyQObject::fooChanged);
|
||||||
Q_OBJECT_BINDABLE_PROPERTY(MyQObject, int, barData, &MyQObject::barChanged);
|
Q_OBJECT_BINDABLE_PROPERTY(MyQObject, int, barData, &MyQObject::barChanged);
|
||||||
Q_OBJECT_BINDABLE_PROPERTY(MyQObject, int, readData, &MyQObject::readChanged);
|
Q_OBJECT_BINDABLE_PROPERTY(MyQObject, int, readData, &MyQObject::readChanged);
|
||||||
|
Q_OBJECT_COMPUTED_PROPERTY(MyQObject, int, computedData, &MyQObject::computed);
|
||||||
};
|
};
|
||||||
|
|
||||||
void tst_QProperty::testNewStuff()
|
void tst_QProperty::testNewStuff()
|
||||||
@ -1049,6 +1053,16 @@ void tst_QProperty::testNewStuff()
|
|||||||
QCOMPARE(object.foo(), 0);
|
QCOMPARE(object.foo(), 0);
|
||||||
object.readData.setValue(10);
|
object.readData.setValue(10);
|
||||||
QCOMPARE(object.foo(), 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()
|
void tst_QProperty::qobjectObservers()
|
||||||
|
Loading…
Reference in New Issue
Block a user