diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp index 8f95b5fd5e..607048d94b 100644 --- a/src/corelib/kernel/qproperty.cpp +++ b/src/corelib/kernel/qproperty.cpp @@ -42,9 +42,12 @@ #include #include +#include QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcQPropertyBinding, "qt.qproperty.binding"); + using namespace QtPrivate; void QPropertyBindingPrivatePtr::destroyAndFreeMemory() @@ -2131,6 +2134,36 @@ bool isPropertyInBindingWrapper(const QUntypedPropertyData *property) bindingStatus.currentCompatProperty->property == property; } +namespace BindableWarnings { + +void printUnsuitableBindableWarning(QAnyStringView prefix, BindableWarnings::Reason reason) +{ + switch (reason) { + case QtPrivate::BindableWarnings::NonBindableInterface: + qCWarning(lcQPropertyBinding).noquote() << prefix.toString() + << "The QBindable does not allow interaction with the binding."; + break; + case QtPrivate::BindableWarnings::ReadOnlyInterface: + qCWarning(lcQPropertyBinding).noquote() << prefix.toString() + << "The QBindable is read-only."; + break; + default: + case QtPrivate::BindableWarnings::InvalidInterface: + qCWarning(lcQPropertyBinding).noquote() << prefix.toString() + << "The QBindable is invalid."; + break; + } +} + +void printMetaTypeMismatch(QMetaType actual, QMetaType expected) +{ + qCWarning(lcQPropertyBinding) << "setBinding: Could not set binding as the property expects it to be of type" + << actual.name() + << "but got" << expected.name() << "instead."; +} + +} // namespace BindableWarnings end + } // namespace QtPrivate end QT_END_NAMESPACE diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h index 9f15a2de7d..9be10c0a31 100644 --- a/src/corelib/kernel/qproperty.h +++ b/src/corelib/kernel/qproperty.h @@ -43,9 +43,7 @@ #include #include #include -#include #include -#include #include @@ -555,6 +553,15 @@ public: } +namespace QtPrivate { +// used in Q(Untyped)Bindable to print warnings about various binding errors +namespace BindableWarnings { +enum Reason { InvalidInterface, NonBindableInterface, ReadOnlyInterface }; +Q_CORE_EXPORT void printUnsuitableBindableWarning(QAnyStringView prefix, Reason reason); +Q_CORE_EXPORT void printMetaTypeMismatch(QMetaType actual, QMetaType expected); +} +} + class QUntypedBindable { protected: @@ -600,6 +607,11 @@ public: { if (iface) iface->setObserver(data, observer); +#ifndef QT_NO_DEBUG + else + QtPrivate::BindableWarnings::printUnsuitableBindableWarning("observe:", + QtPrivate::BindableWarnings::InvalidInterface); +#endif } template @@ -619,16 +631,31 @@ public: QUntypedPropertyBinding binding() const { - if (!iface->getBinding) + if (!isBindable()) { +#ifndef QT_NO_DEBUG + QtPrivate::BindableWarnings::printUnsuitableBindableWarning("binding: ", + QtPrivate::BindableWarnings::NonBindableInterface); +#endif return QUntypedPropertyBinding(); + } return iface->getBinding(data); } bool setBinding(const QUntypedPropertyBinding &binding) { - if (!iface->setBinding) + if (isReadOnly()) { +#ifndef QT_NO_DEBUG + const auto errorType = iface ? QtPrivate::BindableWarnings::ReadOnlyInterface : + QtPrivate::BindableWarnings::InvalidInterface; + QtPrivate::BindableWarnings::printUnsuitableBindableWarning("setBinding: Could not set binding via bindable interface.", errorType); +#endif return false; - if (!binding.isNull() && binding.valueMetaType() != iface->metaType()) + } + if (!binding.isNull() && binding.valueMetaType() != iface->metaType()) { +#ifndef QT_NO_DEBUG + QtPrivate::BindableWarnings::printMetaTypeMismatch(iface->metaType(), binding.valueMetaType()); +#endif return false; + } iface->setBinding(data, binding); return true; } @@ -675,7 +702,16 @@ public: QPropertyBinding setBinding(const QPropertyBinding &binding) { Q_ASSERT(!iface || binding.isNull() || binding.valueMetaType() == iface->metaType()); - return (iface && iface->setBinding) ? static_cast &&>(iface->setBinding(data, binding)) : QPropertyBinding(); + + if (iface && iface->setBinding) + return static_cast &&>(iface->setBinding(data, binding)); +#ifndef QT_NO_DEBUG + if (!iface) + QtPrivate::BindableWarnings::printUnsuitableBindableWarning("setBinding", QtPrivate::BindableWarnings::InvalidInterface); + else + QtPrivate::BindableWarnings::printUnsuitableBindableWarning("setBinding: Could not set binding via bindable interface.", QtPrivate::BindableWarnings::ReadOnlyInterface); +#endif + return QPropertyBinding(); } #ifndef Q_CLANG_QDOC template diff --git a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp index cdb68c7faa..b1f5a780a6 100644 --- a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp +++ b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp @@ -1065,6 +1065,25 @@ void tst_QProperty::quntypedBindableApi() QCOMPARE(iprop.value(), 42); QUntypedBindable propLess; QVERIFY(propLess.takeBinding().isNull()); + + QUntypedBindable invalidBindable; +#ifndef QT_NO_DEBUG + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "setBinding: Could not set binding via bindable interface. The QBindable is invalid."); +#endif + invalidBindable.setBinding(Qt::makePropertyBinding(iprop)); + + QUntypedBindable readOnlyBindable(static_cast *>(&iprop) ); + QVERIFY(readOnlyBindable.isReadOnly()); +#ifndef QT_NO_DEBUG + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "setBinding: Could not set binding via bindable interface. The QBindable is read-only."); +#endif + readOnlyBindable.setBinding(Qt::makePropertyBinding(iprop)); + + QProperty fprop; +#ifndef QT_NO_DEBUG + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "setBinding: Could not set binding as the property expects it to be of type int but got float instead."); +#endif + bindable.setBinding(Qt::makePropertyBinding(fprop)); } void tst_QProperty::readonlyConstQBindable() @@ -1216,6 +1235,9 @@ void tst_QProperty::qobjectBindableSignalTakingNewValue() void tst_QProperty::testNewStuff() { MyQObject testReadOnly; +#ifndef QT_NO_DEBUG + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "setBinding: Could not set binding via bindable interface. The QBindable is read-only."); +#endif testReadOnly.bindableFoo().setBinding([](){return 42;}); auto bindable = const_cast(testReadOnly).bindableFoo(); QVERIFY(bindable.hasBinding());