moc: Allow writing properties through bindables

BINDABLE should generally behave the same as MEMBER if "WRITE default",
except where it cannot. In particular we cannot know if any NOTIFY
signal should be sent from the synthetic WRITE accessor.

[ChangeLog][QtCore] moc will now synthesize WRITE accessors for
properties with BINDABLE if you specify "WRITE default".

Task-number: QTBUG-97249
Change-Id: I883c40ba0dda7989c840971860addaeaa75a8c83
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Ulf Hermann 2022-06-21 11:54:18 +02:00
parent d56f80fd9a
commit 66a30b9a33
4 changed files with 77 additions and 23 deletions

View File

@ -53,7 +53,12 @@
argument, either of the property's type or a pointer or reference
to that type. e.g., QWidget::enabled has the \c WRITE function
QWidget::setEnabled(). Read-only properties do not need \c WRITE
functions. e.g., QWidget::focus has no \c WRITE function.
functions. e.g., QWidget::focus has no \c WRITE function. If you specify
both a \c BINDABLE and \c{WRITE default}, a \c WRITE accessor will be
generated from the \c BINDABLE. The generated \c WRITE accessor will \e not
explicitly emit any signal declared with \c NOTIFY. You should register
the signal as change handler to the \c BINDABLE, for example using
\l{Q_OBJECT_BINDABLE_PROPERTY}.
\li A \c MEMBER variable association is required if no \c READ accessor
function is specified. This makes the given member variable

View File

@ -1315,6 +1315,12 @@ void Generator::generateStaticMetacall()
if (cdef->enumDeclarations.value(p.type, false)) {
fprintf(out, " case %d: %s%s(QFlag(*reinterpret_cast<int*>(_v))); break;\n",
propindex, prefix.constData(), p.write.constData());
} else if (p.write == "default") {
fprintf(out, " case %d: {\n", propindex);
fprintf(out, " %s%s().setValue(*reinterpret_cast< %s*>(_v));\n",
prefix.constData(), p.bind.constData(), p.type.constData());
fprintf(out, " break;\n");
fprintf(out, " }\n");
} else if (!p.write.isEmpty()) {
fprintf(out, " case %d: %s%s(*reinterpret_cast< %s*>(_v)); break;\n",
propindex, prefix.constData(), p.write.constData(), p.type.constData());

View File

@ -1301,7 +1301,7 @@ void Moc::parsePropertyAttributes(PropertyDef &propDef)
error(1);
} else if (test(DEFAULT)) {
v = lexem();
if (l != "READ")
if (l != "READ" && l != "WRITE")
error(1);
} else {
next(IDENTIFIER);
@ -1387,6 +1387,12 @@ void Moc::parsePropertyAttributes(PropertyDef &propDef)
propDef.read = "";
warning(msg.constData());
}
if (propDef.write == "default" && propDef.bind.isNull()) {
const QByteArray msg = "Property declaration " + propDef.name
+ " is not BINDable but default-WRITEable. WRITE will be ignored.";
propDef.write = "";
warning(msg.constData());
}
}
void Moc::parseProperty(ClassDef *def)

View File

@ -744,7 +744,7 @@ private slots:
void observerMetaCall();
void setQPRopertyBinding();
void privateQPropertyShim();
void readThroughBindable();
void readWriteThroughBindable();
signals:
void sigWithUnsignedArg(unsigned foo);
@ -4339,7 +4339,7 @@ void tst_Moc::privateQPropertyShim()
class BindableOnly : public QObject
{
Q_OBJECT
Q_PROPERTY(int score BINDABLE scoreBindable READ default)
Q_PROPERTY(int score BINDABLE scoreBindable READ default WRITE default)
public:
BindableOnly(QObject *parent = nullptr)
: QObject(parent)
@ -4350,27 +4350,64 @@ private:
QProperty<int> m_score;
};
void tst_Moc::readThroughBindable()
class BindableAndNotifyable : public QObject
{
BindableOnly o;
Q_OBJECT
Q_PROPERTY(int score BINDABLE scoreBindable NOTIFY scoreChanged READ default WRITE default)
public:
BindableAndNotifyable(QObject *parent = nullptr)
: QObject(parent)
, m_score(4)
{}
QBindable<int> scoreBindable() { return QBindable<int>(&m_score); }
signals:
void scoreChanged();
private:
QProperty<int> m_score;
};
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);
void tst_Moc::readWriteThroughBindable()
{
{
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());
QVERIFY(p.isWritable());
QCOMPARE(p.read(&o), 5);
QVERIFY(o.setProperty("score", 6));
QCOMPARE(o.property("score").toInt(), 6);
QVERIFY(p.write(&o, 7));
QCOMPARE(p.read(&o), 7);
}
{
BindableAndNotifyable 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());
QVERIFY(p.isWritable());
QCOMPARE(p.read(&o), 5);
QVERIFY(o.setProperty("score", 6));
QCOMPARE(o.property("score").toInt(), 6);
QVERIFY(p.write(&o, 7));
QCOMPARE(p.read(&o), 7);
}
}
QTEST_MAIN(tst_Moc)