diff --git a/src/corelib/doc/src/objectmodel/properties.qdoc b/src/corelib/doc/src/objectmodel/properties.qdoc index 94078b5b6c..7276e8d86a 100644 --- a/src/corelib/doc/src/objectmodel/properties.qdoc +++ b/src/corelib/doc/src/objectmodel/properties.qdoc @@ -44,7 +44,9 @@ specified. It is for reading the property value. Ideally, a const function is used for this purpose, and it must return either the property's type or a const reference to that type. e.g., QWidget::focus is a read-only - property with \c READ function, QWidget::hasFocus(). + property with \c READ function, QWidget::hasFocus(). If a \c BINDABLE is + specified, you can write \c{READ default} to have the \c READ accessor + generated from the \c BINDABLE. \li A \c WRITE accessor function is optional. It is for setting the property value. It must return void and must take exactly one diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index 9bcddd8670..d03d8513db 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -1297,6 +1297,9 @@ void Generator::generateStaticMetacall() else if (cdef->enumDeclarations.value(p.type, false)) fprintf(out, " case %d: *reinterpret_cast(_v) = QFlag(%s%s()); break;\n", propindex, prefix.constData(), p.read.constData()); + else if (p.read == "default") + fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s().value(); break;\n", + propindex, p.type.constData(), prefix.constData(), p.bind.constData()); else if (!p.read.isEmpty()) fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s(); break;\n", propindex, p.type.constData(), prefix.constData(), p.read.constData()); diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index f107a6f0d8..5ea79a4280 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -1306,6 +1306,10 @@ void Moc::parsePropertyAttributes(PropertyDef &propDef) v = lexem(); if (l != "REVISION") error(1); + } else if (test(DEFAULT)) { + v = lexem(); + if (l != "READ") + error(1); } else { next(IDENTIFIER); v = lexem(); @@ -1384,6 +1388,12 @@ void Moc::parsePropertyAttributes(PropertyDef &propDef) propDef.constant = false; warning(msg.constData()); } + if (propDef.read == "default" && propDef.bind.isNull()) { + const QByteArray msg = "Property declaration " + propDef.name + + " is not BINDable but default-READable. READ will be ignored."; + propDef.read = ""; + warning(msg.constData()); + } } void Moc::parseProperty(ClassDef *def) diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp index f56e2c3024..89794ecc1d 100644 --- a/tests/auto/tools/moc/tst_moc.cpp +++ b/tests/auto/tools/moc/tst_moc.cpp @@ -743,6 +743,7 @@ private slots: void observerMetaCall(); void setQPRopertyBinding(); void privateQPropertyShim(); + void readThroughBindable(); signals: void sigWithUnsignedArg(unsigned foo); @@ -4329,6 +4330,44 @@ void tst_Moc::privateQPropertyShim() QCOMPARE(testObject.priv.testProperty2.value(), 42); } + +class BindableOnly : public QObject +{ + Q_OBJECT + Q_PROPERTY(int score BINDABLE scoreBindable READ default) +public: + BindableOnly(QObject *parent = nullptr) + : QObject(parent) + , m_score(4) + {} + QBindable scoreBindable() { return QBindable(&m_score); } +private: + QProperty m_score; +}; + + +void tst_Moc::readThroughBindable() +{ + BindableOnly o; + + QCOMPARE(o.scoreBindable().value(), 4); + QCOMPARE(o.property("score").toInt(), 4); + o.scoreBindable().setValue(5); + QCOMPARE(o.scoreBindable().value(), 5); + QCOMPARE(o.property("score").toInt(), 5); + + + const QMetaObject *mo = o.metaObject(); + const int i = mo->indexOfProperty("score"); + QVERIFY(i > 0); + + QMetaProperty p = mo->property(i); + QCOMPARE(p.name(), "score"); + + QVERIFY(p.isValid()); + QCOMPARE(p.read(&o), 5); +} + QTEST_MAIN(tst_Moc) // the generated code must compile with QT_NO_KEYWORDS