moc: Allow reading property values through bindables

The behavior is similar to MEMBER: If the READ accessor is "default",
synthesize it using the bindable.

[ChangeLog][QtCore] You can now specify "READ default" in a Q_PROPERTY
if you also specify a BINDABLE. moc will synthesize a READ accessor in
that case.

Task-number: QTBUG-97249
Change-Id: I4a275adabaed12df95dac505095f847c4be65dfe
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
bb10
Ulf Hermann 2022-06-09 13:23:33 +02:00
parent 073214fdf9
commit 2c0518eb62
4 changed files with 55 additions and 1 deletions

View File

@ -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

View File

@ -1297,6 +1297,9 @@ void Generator::generateStaticMetacall()
else if (cdef->enumDeclarations.value(p.type, false))
fprintf(out, " case %d: *reinterpret_cast<int*>(_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());

View File

@ -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)

View File

@ -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<int> scoreBindable() { return QBindable<int>(&m_score); }
private:
QProperty<int> 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