From 927647cd032cd2e43bae3184b879586849ffee50 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 29 Aug 2020 14:02:36 +0200 Subject: [PATCH] Fix QPropertyAlias to work with all kinds of properties So far QPropertyAlias was limited to working with QProperty. Change the implementation, so it can be constructed from any property or even a QBindable. Change-Id: I175cffe94a9ef332367d39faa976eb065b0e6ffe Reviewed-by: Qt CI Bot Reviewed-by: Fabian Kosmale --- src/corelib/kernel/qproperty.h | 258 +++++++++--------- .../kernel/qproperty/tst_qproperty.cpp | 37 +++ 2 files changed, 163 insertions(+), 132 deletions(-) diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h index 7ba2d9514a..2941de2c54 100644 --- a/src/corelib/kernel/qproperty.h +++ b/src/corelib/kernel/qproperty.h @@ -473,138 +473,6 @@ namespace Qt { } } -template -class QPropertyAlias : public QPropertyObserver -{ - Q_DISABLE_COPY_MOVE(QPropertyAlias) - QProperty *aliasedProperty() const - { - return static_cast *>(QPropertyObserver::aliasedProperty()); - } - -public: - QPropertyAlias(QProperty *property) - : QPropertyObserver(property) - { - if (property) - setSource(*property); - } - - QPropertyAlias(QPropertyAlias *alias) - : QPropertyAlias(alias->aliasedProperty()) - {} - - T value() const - { - if (auto *p = aliasedProperty()) - return p->value(); - return T(); - } - - operator T() const { return value(); } - - void setValue(T &&newValue) - { - if (auto *p = aliasedProperty()) - p->setValue(std::move(newValue)); - } - - void setValue(const T &newValue) - { - if (auto *p = aliasedProperty()) - p->setValue(newValue); - } - - QPropertyAlias &operator=(T &&newValue) - { - if (auto *p = aliasedProperty()) - *p = std::move(newValue); - return *this; - } - - QPropertyAlias &operator=(const T &newValue) - { - if (auto *p = aliasedProperty()) - *p = newValue; - return *this; - } - - QPropertyBinding setBinding(const QPropertyBinding &newBinding) - { - if (auto *p = aliasedProperty()) - return p->setBinding(newBinding); - return QPropertyBinding(); - } - - QPropertyBinding setBinding(QPropertyBinding &&newBinding) - { - if (auto *p = aliasedProperty()) - return p->setBinding(std::move(newBinding)); - return QPropertyBinding(); - } - - bool setBinding(const QUntypedPropertyBinding &newBinding) - { - if (auto *p = aliasedProperty()) - return p->setBinding(newBinding); - return false; - } - -#ifndef Q_CLANG_QDOC - template - QPropertyBinding setBinding(Functor &&f, - const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION, - std::enable_if_t> * = nullptr) - { - return setBinding(Qt::makePropertyBinding(std::forward(f), location)); - } -#else - template - QPropertyBinding setBinding(Functor f); -#endif - - bool hasBinding() const - { - if (auto *p = aliasedProperty()) - return p->hasBinding(); - return false; - } - - QPropertyBinding binding() const - { - if (auto *p = aliasedProperty()) - return p->binding(); - return QPropertyBinding(); - } - - QPropertyBinding takeBinding() - { - if (auto *p = aliasedProperty()) - return p->takeBinding(); - return QPropertyBinding(); - } - - template - QPropertyChangeHandler onValueChanged(Functor f) - { - if (auto *p = aliasedProperty()) - return p->onValueChanged(f); - return QPropertyChangeHandler(f); - } - - template - QPropertyChangeHandler subscribe(Functor f) - { - if (auto *p = aliasedProperty()) - return p->subscribe(f); - return QPropertyChangeHandler(f); - } - - bool isValid() const - { - return aliasedProperty() != nullptr; - } -}; namespace QtPrivate { @@ -677,6 +545,9 @@ class QUntypedBindable protected: QUntypedPropertyData *data = nullptr; const QtPrivate::QBindableInterface *iface = nullptr; + constexpr QUntypedBindable(QUntypedPropertyData *d, const QtPrivate::QBindableInterface *i) + : data(d), iface(i) + {} public: constexpr QUntypedBindable() = default; @@ -739,6 +610,11 @@ public: template class QBindable : public QUntypedBindable { + template + friend class QPropertyAlias; + constexpr QBindable(QUntypedPropertyData *d, const QtPrivate::QBindableInterface *i) + : QUntypedBindable(d, i) + {} public: using QUntypedBindable::QUntypedBindable; explicit QBindable(const QUntypedBindable &b) : QUntypedBindable(b) @@ -777,6 +653,124 @@ public: #endif }; +template +class QPropertyAlias : public QPropertyObserver +{ + Q_DISABLE_COPY_MOVE(QPropertyAlias) + const QtPrivate::QBindableInterface *iface = nullptr; + +public: + QPropertyAlias(QProperty *property) + : QPropertyObserver(property), + iface(&QtPrivate::QBindableInterfaceForProperty>::iface) + { + if (iface) + iface->setObserver(aliasedProperty(), this); + } + + template + QPropertyAlias(Property *property) + : QPropertyObserver(property), + iface(&QtPrivate::QBindableInterfaceForProperty::iface) + { + if (iface) + iface->setObserver(aliasedProperty(), this); + } + + QPropertyAlias(QPropertyAlias *alias) + : QPropertyObserver(alias->aliasedProperty()), + iface(alias->iface) + { + if (iface) + iface->setObserver(aliasedProperty(), this); + } + + QPropertyAlias(const QBindable &property) + : QPropertyObserver(property.data), + iface(property.iface) + { + if (iface) + iface->setObserver(aliasedProperty(), this); + } + + T value() const + { + T t = T(); + if (auto *p = aliasedProperty()) + iface->getter(p, &t); + return t; + } + + operator T() const { return value(); } + + void setValue(const T &newValue) + { + if (auto *p = aliasedProperty()) + iface->setter(p, &newValue); + } + + QPropertyAlias &operator=(const T &newValue) + { + setValue(newValue); + return *this; + } + + QPropertyBinding setBinding(const QPropertyBinding &newBinding) + { + return QBindable(aliasedProperty(), iface).setBinding(newBinding); + } + + bool setBinding(const QUntypedPropertyBinding &newBinding) + { + return QBindable(aliasedProperty(), iface).setBinding(newBinding); + } + +#ifndef Q_CLANG_QDOC + template + QPropertyBinding setBinding(Functor &&f, + const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION, + std::enable_if_t> * = nullptr) + { + return setBinding(Qt::makePropertyBinding(std::forward(f), location)); + } +#else + template + QPropertyBinding setBinding(Functor f); +#endif + + bool hasBinding() const + { + return QBindable(aliasedProperty(), iface).hasBinding(); + } + + QPropertyBinding binding() const + { + return QBindable(aliasedProperty(), iface).binding(); + } + + QPropertyBinding takeBinding() + { + return QBindable(aliasedProperty(), iface).takeBinding(); + } + + template + QPropertyChangeHandler onValueChanged(Functor f) + { + return QBindable(aliasedProperty(), iface).onValueChanged(f); + } + + template + QPropertyChangeHandler subscribe(Functor f) + { + return QBindable(aliasedProperty(), iface).subscribe(f); + } + + bool isValid() const + { + return aliasedProperty() != nullptr; + } +}; + struct QBindingStatus; struct QBindingStorageData; diff --git a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp index f5652eb599..9bc2ce26b6 100644 --- a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp +++ b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp @@ -78,6 +78,7 @@ private slots: void qobjectObservers(); void compatBindings(); void metaProperty(); + void aliasOnMetaProperty(); }; void tst_QProperty::functorBinding() @@ -1210,6 +1211,42 @@ void tst_QProperty::metaProperty() QCOMPARE(object.fooData.value(), 1); } +void tst_QProperty::aliasOnMetaProperty() +{ + MyQObject object; + QPropertyAlias alias(object.bindableFoo()); + + QVERIFY(alias.isValid()); + QCOMPARE(alias.value(), object.foo()); + QVERIFY(!alias.hasBinding()); + + object.setFoo(42); + QCOMPARE(alias.value(), 42); + + auto f = [&object]() -> int { + return object.barData; + }; + object.bindableFoo().setBinding(f); + QVERIFY(alias.hasBinding()); + QCOMPARE(alias.value(), object.bar()); + + object.setBar(111); + QCOMPARE(alias.value(), 111); + + int changedCount = 0; + auto observer = alias.onValueChanged([&changedCount]() { ++changedCount; }); + QCOMPARE(changedCount, 0); + object.setBar(666); + QCOMPARE(changedCount, 1); + + alias.setBinding([&object]() { return object.read(); }); + QCOMPARE(changedCount, 2); + QCOMPARE(alias.value(), 0); + object.readData = 100; + QCOMPARE(changedCount, 3); + QCOMPARE(alias.value(), 100); +} + QTEST_MAIN(tst_QProperty); #include "tst_qproperty.moc"