From 66a30b9a33aac288335f32ea9dc8dd8542abf69f Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 21 Jun 2022 11:54:18 +0200 Subject: [PATCH] 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 --- .../doc/src/objectmodel/properties.qdoc | 7 +- src/tools/moc/generator.cpp | 6 ++ src/tools/moc/moc.cpp | 8 +- tests/auto/tools/moc/tst_moc.cpp | 79 ++++++++++++++----- 4 files changed, 77 insertions(+), 23 deletions(-) diff --git a/src/corelib/doc/src/objectmodel/properties.qdoc b/src/corelib/doc/src/objectmodel/properties.qdoc index 7276e8d86ad..7ca3e25b004 100644 --- a/src/corelib/doc/src/objectmodel/properties.qdoc +++ b/src/corelib/doc/src/objectmodel/properties.qdoc @@ -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 diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index f030bc8918c..0e8ba0bb727 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -1315,6 +1315,12 @@ void Generator::generateStaticMetacall() if (cdef->enumDeclarations.value(p.type, false)) { fprintf(out, " case %d: %s%s(QFlag(*reinterpret_cast(_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()); diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index a14f0c1ccd8..8a5abd30201 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -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) diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp index 97575f5bb75..48249eec0c7 100644 --- a/tests/auto/tools/moc/tst_moc.cpp +++ b/tests/auto/tools/moc/tst_moc.cpp @@ -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 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 scoreBindable() { return QBindable(&m_score); } +signals: + void scoreChanged(); +private: + QProperty 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)