qmltc: Enforce basic required properties
Types in QML can expose "required" properties. A required property is a property that should be initialized when an instance of the type is built. Instantiating a type without initializing its required properties is an hard error. Currently, `qmltc` generated types do not respect the semantic of "required" properties. Instead, `qmltc` will generally default-initialize a required property without notifying the user of the error. `qmtlc`, so as to respect the semantic of required properties, will now require the user to pass an initial value for all required properties at construction time. To do so, `qmltc` will now generate a new inner record, `RequiredPropertiesBundle`, for each compiled top-level type, that contains the required amount of data to initialize each top-level required property that is reachable from the compiled type. An instance of `RequiredPropertiesBundle` will be required, as long as the type presents at least one required property, in the user-facing constructor for the generated type. The information stored in the instance will later be used to provide an initial value for each required property during the construction of the component. An intermediate representation for `RequiredPropertiesBundle` was added to "qmltcoutputir.h". `QmltcCodeWriter`, the component responsible for writing the final C++ code, was modified to take into consideration the presence, or lack thereof, of a `RequiredPropertiesBundle` and output the necessary code when required. The code taking care of populating the various IRs was modified to populate a `RequiredPropertiesBundle` for top-level components as necessary. Similarly, the code populating the parameters of the user-facing constructor was modified to require an instance of `RequiredPropertiesBundle` when necessary. The code that populates the body of the user-facing constructor for top-level types was modified to make use of the parameter by tying into the existing structure for setting initial values. `qmltc` uses a user-provided callback to allow the user to set the initial values for properties when constructing a top-level component. The body of the user-facing constructor was modified to compose the user-provided callback with a callable that sets the initial values for top-level required properties based on the bundle of data in the new `RequiredPropertiesBundle` instance. The code that populates the body of the user-facing constructor was moved into its own free-function, `compileRootExternalConstructorBody`, to be slightly more explicit about the structure of the code. A new test was provided to evaluate, some basic cases for the new behavior. Some pre-existing tests, which made use of required properties, were modified to comply with the new generated API. The documentation for `qmltc` was modified with a note about the new behavior. Task-number: QTBUG-120698 Change-Id: I1e916dcd91ae976629dad8adc7eacc6390bce7e9 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
This commit is contained in:
parent
6a8b6dbc84
commit
fe6f283a5b
|
@ -115,6 +115,10 @@ object in C++ is equivalent to creating a new object through
|
|||
QQmlComponent::create(). Once created, the object could be manipulated from C++
|
||||
or, for example, combined with QQuickWindow to be drawn on screen.
|
||||
|
||||
If a compiled type exposes some required properties, `qmltc` will
|
||||
require an initial value for those properties in the constructor for
|
||||
the generated object.
|
||||
|
||||
Additionally, the constructor for a qmltc object can be provided with
|
||||
with a callback to set up initial values for the component's
|
||||
properties.
|
||||
|
|
|
@ -23,6 +23,7 @@ set(cpp_sources
|
|||
cpptypes/typewithnamespace.h cpptypes/typewithnamespace.cpp
|
||||
cpptypes/typewithsignal.h
|
||||
cpptypes/custominitialization.h
|
||||
cpptypes/typewithrequiredproperties.h
|
||||
)
|
||||
|
||||
set(qml_sources
|
||||
|
@ -134,6 +135,8 @@ set(qml_sources
|
|||
NamespacedTypes.qml
|
||||
|
||||
badFile.qml
|
||||
|
||||
requiredProperties.qml
|
||||
)
|
||||
|
||||
set(js_sources
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qproperty.h>
|
||||
#include <QtQuick/qquickitem.h>
|
||||
#include <QtQml/qqmllist.h>
|
||||
#include <QtQml/qqmlregistration.h>
|
||||
|
||||
#ifndef TYPEWITHREQUIREDPROPERTIES_H_
|
||||
# define TYPEWITHREQUIREDPROPERTIES_H_
|
||||
|
||||
class ExtensionTypeWithRequiredProperties : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
|
||||
Q_PROPERTY(double requiredPropertyFromExtension READ getRequiredPropertyFromExtension WRITE
|
||||
setRequiredPropertyFromExtension REQUIRED)
|
||||
|
||||
QProperty<double> m_requiredPropertyFromExtension{};
|
||||
|
||||
public:
|
||||
ExtensionTypeWithRequiredProperties(QObject *parent = nullptr) : QObject(parent) { }
|
||||
|
||||
double getRequiredPropertyFromExtension() const { return m_requiredPropertyFromExtension; }
|
||||
void setRequiredPropertyFromExtension(double v) { m_requiredPropertyFromExtension = v; }
|
||||
};
|
||||
|
||||
class TypeWithRequiredProperties : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
Q_PROPERTY(QQuickItem *inheritedRequiredProperty READ getInheritedRequiredProperty WRITE
|
||||
setInheritedRequiredProperty REQUIRED)
|
||||
Q_PROPERTY(int inheritedRequiredPropertyThatWillBeBound READ
|
||||
getInheritedRequiredPropertyThatWillBeBound WRITE
|
||||
setInheritedRequiredPropertyThatWillBeBound REQUIRED)
|
||||
Q_PROPERTY(int nonRequiredInheritedPropertyThatWillBeMarkedRequired READ
|
||||
getNonRequiredInheritedPropertyThatWillBeMarkedRequired WRITE
|
||||
setNonRequiredInheritedPropertyThatWillBeMarkedRequired REQUIRED)
|
||||
|
||||
QML_EXTENDED(ExtensionTypeWithRequiredProperties)
|
||||
|
||||
QProperty<QQuickItem *> m_inheritedRequiredProperty{};
|
||||
QProperty<int> m_inheritedRequiredPropertyThatWillBeBound{};
|
||||
QProperty<int> m_nonRequiredInheritedPropertyThatWillBeMarkedRequired{};
|
||||
|
||||
public:
|
||||
TypeWithRequiredProperties(QObject *parent = nullptr) : QObject(parent) { }
|
||||
|
||||
QQuickItem *getInheritedRequiredProperty() const { return m_inheritedRequiredProperty; }
|
||||
void setInheritedRequiredProperty(QQuickItem *v) { m_inheritedRequiredProperty = v; }
|
||||
|
||||
int getInheritedRequiredPropertyThatWillBeBound() const
|
||||
{
|
||||
return m_inheritedRequiredPropertyThatWillBeBound;
|
||||
}
|
||||
void setInheritedRequiredPropertyThatWillBeBound(int v)
|
||||
{
|
||||
m_inheritedRequiredPropertyThatWillBeBound = v;
|
||||
}
|
||||
|
||||
int getNonRequiredInheritedPropertyThatWillBeMarkedRequired() const
|
||||
{
|
||||
return m_nonRequiredInheritedPropertyThatWillBeMarkedRequired;
|
||||
}
|
||||
void setNonRequiredInheritedPropertyThatWillBeMarkedRequired(int v)
|
||||
{
|
||||
m_nonRequiredInheritedPropertyThatWillBeMarkedRequired = v;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // TYPEWITHREQUIREDPROPERTIES_H_
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
import QtQuick
|
||||
import QmltcTests 1.0
|
||||
|
||||
TypeWithRequiredProperties {
|
||||
id: self
|
||||
required property int primitiveType
|
||||
required property list<int> valueList
|
||||
required property list<Item> objectList
|
||||
|
||||
property int propertyThatWillBeMarkedRequired
|
||||
|
||||
// This is already bound so it should not appear as part of the
|
||||
// bundle.
|
||||
inheritedRequiredPropertyThatWillBeBound : 10
|
||||
|
||||
// This should be ignored as it alias a required property we are
|
||||
// already going to consider. It should thus not appear as part of
|
||||
// the bundle.
|
||||
property alias aliasToRequiredProperty : self.primitiveType
|
||||
|
||||
required propertyThatWillBeMarkedRequired
|
||||
required nonRequiredInheritedPropertyThatWillBeMarkedRequired
|
||||
|
||||
property alias aliasToRequiredInner: inner.requiredInner
|
||||
|
||||
// This should be ignored as the underlying property is already bound.
|
||||
property alias aliasToRequiredBoundedInner: inner.requiredBoundedInner
|
||||
|
||||
property alias aliasToInnerThatWillBeMarkedRequired: inner.nonRequiredInner
|
||||
required aliasToInnerThatWillBeMarkedRequired
|
||||
|
||||
property int notRequired
|
||||
property alias requiredAliasToUnrequiredProperty : self.notRequired
|
||||
required requiredAliasToUnrequiredProperty
|
||||
|
||||
// When we have an alias to a required property in the same scope
|
||||
// we exclude the alias in favor of setting the property directly.
|
||||
// See for example aliasToRequiredProperty in this file.
|
||||
//
|
||||
// The following alias should instead be picked up, as it point to
|
||||
// an inner scope.
|
||||
// Nonetheless, an initial implementation had a bug that would
|
||||
// discard the alias as long as a property with the same name as
|
||||
// the target was present in the same scope.
|
||||
//
|
||||
// The following alias tests this initially failing case.
|
||||
property alias aliasToPropertyThatShadows: inner.primitiveType
|
||||
|
||||
property Item children : Item {
|
||||
id: inner
|
||||
required property int requiredInner
|
||||
property int nonRequiredInner
|
||||
required property int requiredBoundedInner
|
||||
requiredBoundedInner: 43
|
||||
|
||||
required property int primitiveType
|
||||
}
|
||||
}
|
|
@ -90,6 +90,7 @@
|
|||
#include "qmltablemodel.h"
|
||||
#include "stringtourl.h"
|
||||
#include "signalconnections.h"
|
||||
#include "requiredproperties.h"
|
||||
|
||||
// Qt:
|
||||
#include <QtCore/qstring.h>
|
||||
|
@ -202,6 +203,7 @@ void tst_qmltc::initTestCase()
|
|||
QUrl("qrc:/qt/qml/QmltcTests/calqlatrBits.qml"),
|
||||
QUrl("qrc:/qt/qml/QmltcTests/valueTypeListProperty.qml"),
|
||||
QUrl("qrc:/qt/qml/QmltcTests/appendToQQmlListProperty.qml"),
|
||||
QUrl("qrc:/qt/qml/QmltcTests/requiredProperties.qml"),
|
||||
};
|
||||
|
||||
QQmlEngine e;
|
||||
|
@ -845,8 +847,7 @@ void tst_qmltc::customInitialization()
|
|||
|
||||
QQmlEngine e;
|
||||
PREPEND_NAMESPACE(qtbug120700_main)
|
||||
created(&e, nullptr, [valueToTest, &firstItem, &secondItem](auto& component) {
|
||||
component.setSomeValue(valueToTest);
|
||||
created(&e, {valueToTest} ,nullptr, [valueToTest, &firstItem, &secondItem](auto& component) {
|
||||
component.setSomeComplexValueThatWillBeSet(valueToTest);
|
||||
component.setPropertyFromExtension(static_cast<double>(valueToTest));
|
||||
component.setDefaultedBindable(static_cast<double>(valueToTest));
|
||||
|
@ -886,6 +887,53 @@ void tst_qmltc::customInitialization()
|
|||
QCOMPARE(created.getCppObjectList().toList<QList<QQuickItem*>>(), QList({&firstItem, &secondItem}));
|
||||
}
|
||||
|
||||
void tst_qmltc::requiredPropertiesInitialization()
|
||||
{
|
||||
QQuickItem item{};
|
||||
|
||||
int aliasToInnerThatWillBeMarkedRequired = 10;
|
||||
int aliasToPropertyThatShadows = 42;
|
||||
int aliasToRequiredInner = 11;
|
||||
QQuickItem inheritedRequiredProperty{};
|
||||
int nonRequiredInheritedPropertyThatWillBeMarkedRequired = 12;
|
||||
QList<QQuickItem*> objectList{&item};
|
||||
int primitiveType = 13;
|
||||
int propertyThatWillBeMarkedRequired = 14;
|
||||
int requiredAliasToUnrequiredProperty = 15;
|
||||
double requiredPropertyFromExtension = 16.0;
|
||||
QList<int> valueList{1, 2, 3, 4};
|
||||
|
||||
QQmlEngine e;
|
||||
PREPEND_NAMESPACE(requiredProperties) created(
|
||||
&e,
|
||||
{
|
||||
aliasToInnerThatWillBeMarkedRequired,
|
||||
aliasToPropertyThatShadows,
|
||||
aliasToRequiredInner,
|
||||
&inheritedRequiredProperty,
|
||||
nonRequiredInheritedPropertyThatWillBeMarkedRequired,
|
||||
objectList,
|
||||
primitiveType,
|
||||
propertyThatWillBeMarkedRequired,
|
||||
requiredAliasToUnrequiredProperty,
|
||||
requiredPropertyFromExtension,
|
||||
valueList
|
||||
}
|
||||
);
|
||||
|
||||
QCOMPARE(created.aliasToInnerThatWillBeMarkedRequired(), aliasToInnerThatWillBeMarkedRequired);
|
||||
QCOMPARE(created.aliasToPropertyThatShadows(), aliasToPropertyThatShadows);
|
||||
QCOMPARE(created.aliasToRequiredInner(), aliasToRequiredInner);
|
||||
QCOMPARE(created.getInheritedRequiredProperty(), &inheritedRequiredProperty);
|
||||
QCOMPARE(created.getNonRequiredInheritedPropertyThatWillBeMarkedRequired(), nonRequiredInheritedPropertyThatWillBeMarkedRequired);
|
||||
QCOMPARE(created.objectList().toList<QList<QQuickItem*>>(), objectList);
|
||||
QCOMPARE(created.primitiveType(), primitiveType);
|
||||
QCOMPARE(created.propertyThatWillBeMarkedRequired(), propertyThatWillBeMarkedRequired);
|
||||
QCOMPARE(created.requiredAliasToUnrequiredProperty(), requiredAliasToUnrequiredProperty);
|
||||
QCOMPARE(created.property("requiredPropertyFromExtension").toDouble(), requiredPropertyFromExtension);
|
||||
QCOMPARE(created.valueList(), valueList);
|
||||
}
|
||||
|
||||
// QTBUG-104094
|
||||
void tst_qmltc::nonStandardIncludesInsideModule()
|
||||
{
|
||||
|
@ -1132,7 +1180,7 @@ void tst_qmltc::propertyAlias_external()
|
|||
void tst_qmltc::propertyAliasAttribute()
|
||||
{
|
||||
QQmlEngine e;
|
||||
PREPEND_NAMESPACE(propertyAliasAttributes) fromQmltc(&e);
|
||||
PREPEND_NAMESPACE(propertyAliasAttributes) fromQmltc(&e, {""});
|
||||
|
||||
QQmlComponent c(&e);
|
||||
c.loadUrl(QUrl("qrc:/qt/qml/QmltcTests/propertyAliasAttributes.qml"));
|
||||
|
|
|
@ -36,6 +36,7 @@ private slots:
|
|||
void extensionTypeBindings();
|
||||
void visibleAliasMethods(); // QTBUG-103956
|
||||
void customInitialization(); // QTBUG-120700
|
||||
void requiredPropertiesInitialization();
|
||||
void nonStandardIncludesInsideModule(); // QTBUG-104094
|
||||
void specialProperties();
|
||||
void regexpBindings();
|
||||
|
|
|
@ -203,6 +203,22 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code,
|
|||
code.rawAppendToHeader(u""); // blank line
|
||||
}
|
||||
|
||||
void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcRequiredPropertiesBundle &requiredPropertiesBundle)
|
||||
{
|
||||
code.rawAppendToHeader(u"struct " + requiredPropertiesBundle.name + u" {");
|
||||
|
||||
{
|
||||
[[maybe_unused]] QmltcOutputWrapper::HeaderIndentationScope headerIndent(&code);
|
||||
|
||||
for (const auto &member : requiredPropertiesBundle.members) {
|
||||
write(code, member);
|
||||
}
|
||||
}
|
||||
|
||||
code.rawAppendToHeader(u"};"_s);
|
||||
code.rawAppendToHeader(u""); // blank line
|
||||
}
|
||||
|
||||
void QmltcCodeWriter::writeGlobalFooter(QmltcOutputWrapper &code, const QString &sourcePath,
|
||||
const QString &outNamespace)
|
||||
{
|
||||
|
@ -339,6 +355,9 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcType &type,
|
|||
if (!type.propertyInitializer.name.isEmpty())
|
||||
write(code, type.propertyInitializer, type);
|
||||
|
||||
if (type.requiredPropertiesBundle)
|
||||
write(code, *type.requiredPropertiesBundle);
|
||||
|
||||
// NB: when non-document root, the externalCtor won't be public - but we
|
||||
// really don't care about the output format of such types
|
||||
if (!type.ignoreInit && type.externalCtor.access == QQmlJSMetaMethod::Public) {
|
||||
|
|
|
@ -28,6 +28,7 @@ struct QmltcCodeWriter
|
|||
static void write(QmltcOutputWrapper &code, const QmltcVariable &var);
|
||||
static void write(QmltcOutputWrapper &code, const QmltcProperty &prop);
|
||||
static void write(QmltcOutputWrapper &code, const QmltcPropertyInitializer &propertyInitializer, const QmltcType& wrappedType);
|
||||
static void write(QmltcOutputWrapper &code, const QmltcRequiredPropertiesBundle &requiredPropertiesBundle);
|
||||
|
||||
private:
|
||||
static void writeUrl(QmltcOutputWrapper &code, const QmltcMethod &urlMethod); // special
|
||||
|
|
|
@ -23,6 +23,137 @@ bool qIsReferenceTypeList(const QQmlJSMetaProperty &p)
|
|||
return false;
|
||||
}
|
||||
|
||||
static QList<QQmlJSMetaProperty> unboundRequiredProperties(
|
||||
const QQmlJSScope::ConstPtr &type,
|
||||
QmltcTypeResolver *resolver
|
||||
) {
|
||||
QList<QQmlJSMetaProperty> requiredProperties{};
|
||||
|
||||
auto isPropertyRequired = [&type, &resolver](const auto &property) {
|
||||
if (!type->isPropertyRequired(property.propertyName()))
|
||||
return false;
|
||||
|
||||
if (type->hasPropertyBindings(property.propertyName()))
|
||||
return false;
|
||||
|
||||
if (property.isAlias()) {
|
||||
QQmlJSUtils::AliasResolutionVisitor aliasVisitor;
|
||||
|
||||
QQmlJSUtils::ResolvedAlias result =
|
||||
QQmlJSUtils::resolveAlias(resolver, property, type, aliasVisitor);
|
||||
|
||||
if (result.kind != QQmlJSUtils::AliasTarget_Property)
|
||||
return false;
|
||||
|
||||
// If the top level alias targets a property that is in
|
||||
// the top level scope and that property is required, then
|
||||
// we will already pick up the property during one of the
|
||||
// iterations.
|
||||
// Setting the property or the alias is the same so we
|
||||
// discard one of the two, as otherwise we would require
|
||||
// the user to pass two values for the same property ,in
|
||||
// this case the alias.
|
||||
//
|
||||
// For example in:
|
||||
//
|
||||
// ```
|
||||
// Item {
|
||||
// id: self
|
||||
// required property int foo
|
||||
// property alias bar: self.foo
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// Both foo and bar are required but setting one or the
|
||||
// other is the same operation so that we should choose
|
||||
// only one.
|
||||
if (result.owner == type &&
|
||||
type->isPropertyRequired(result.property.propertyName()))
|
||||
return false;
|
||||
|
||||
if (result.owner->hasPropertyBindings(result.property.propertyName()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const auto properties = type->properties();
|
||||
std::copy_if(properties.cbegin(), properties.cend(),
|
||||
std::back_inserter(requiredProperties), isPropertyRequired);
|
||||
std::sort(requiredProperties.begin(), requiredProperties.end(),
|
||||
[](const auto &left, const auto &right) {
|
||||
return left.propertyName() < right.propertyName();
|
||||
});
|
||||
|
||||
return requiredProperties;
|
||||
}
|
||||
|
||||
|
||||
// Populates the internal representation for a
|
||||
// RequiredPropertiesBundle, a class that acts as a bundle of initial
|
||||
// values that should be set for the required properties of a type.
|
||||
static void compileRequiredPropertiesBundle(
|
||||
QmltcType ¤t,
|
||||
const QQmlJSScope::ConstPtr &type,
|
||||
QmltcTypeResolver *resolver
|
||||
) {
|
||||
|
||||
QList<QQmlJSMetaProperty> requiredProperties = unboundRequiredProperties(type, resolver);
|
||||
|
||||
if (requiredProperties.isEmpty())
|
||||
return;
|
||||
|
||||
current.requiredPropertiesBundle.emplace();
|
||||
current.requiredPropertiesBundle->name = u"RequiredPropertiesBundle"_s;
|
||||
|
||||
current.requiredPropertiesBundle->members.reserve(requiredProperties.size());
|
||||
std::transform(requiredProperties.cbegin(), requiredProperties.cend(),
|
||||
std::back_inserter(current.requiredPropertiesBundle->members),
|
||||
[](const QQmlJSMetaProperty &property) {
|
||||
QString type = qIsReferenceTypeList(property)
|
||||
? u"const QList<%1*>&"_s.arg(
|
||||
property.type()->valueType()->internalName())
|
||||
: u"passByConstRefOrValue<%1>"_s.arg(
|
||||
property.type()->augmentedInternalName());
|
||||
return QmltcVariable{ type, property.propertyName() };
|
||||
});
|
||||
}
|
||||
|
||||
static void compileRootExternalConstructorBody(
|
||||
QmltcType& current,
|
||||
const QQmlJSScope::ConstPtr &type
|
||||
) {
|
||||
current.externalCtor.body << u"// document root:"_s;
|
||||
// if it's document root, we want to create our QQmltcObjectCreationBase
|
||||
// that would store all the created objects
|
||||
current.externalCtor.body << u"QQmltcObjectCreationBase<%1> objectHolder;"_s.arg(
|
||||
type->internalName());
|
||||
current.externalCtor.body
|
||||
<< u"QQmltcObjectCreationHelper creator = objectHolder.view();"_s;
|
||||
current.externalCtor.body << u"creator.set(0, this);"_s; // special case
|
||||
|
||||
QString initializerName = u"initializer"_s;
|
||||
if (current.requiredPropertiesBundle) {
|
||||
// Compose new initializer based on the initial values for required properties.
|
||||
current.externalCtor.body << u"auto newInitializer = [&](auto& propertyInitializer) {"_s;
|
||||
for (const auto& member : current.requiredPropertiesBundle->members) {
|
||||
current.externalCtor.body << u" propertyInitializer.%1(requiredPropertiesBundle.%2);"_s.arg(
|
||||
QmltcPropertyData(member.name).write, member.name
|
||||
);
|
||||
}
|
||||
current.externalCtor.body << u" initializer(propertyInitializer);"_s;
|
||||
current.externalCtor.body << u"};"_s;
|
||||
|
||||
initializerName = u"newInitializer"_s;
|
||||
}
|
||||
|
||||
// now call init
|
||||
current.externalCtor.body << current.init.name
|
||||
+ u"(&creator, engine, QQmlContextData::get(engine->rootContext()), /* "
|
||||
u"endInit */ true, %1);"_s.arg(initializerName);
|
||||
};
|
||||
|
||||
Q_LOGGING_CATEGORY(lcQmltcCompiler, "qml.qmltc.compiler", QtWarningMsg);
|
||||
|
||||
const QString QmltcCodeGenerator::privateEngineName = u"ePriv"_s;
|
||||
|
@ -299,11 +430,22 @@ void QmltcCompiler::compileType(
|
|||
u"initializer"_s,
|
||||
u"[](%1&){}"_s.arg(current.propertyInitializer.name));
|
||||
|
||||
current.externalCtor.parameterList = { engine, parent, initializer };
|
||||
current.init.parameterList = { creator, engine, ctxtdata, finalizeFlag, initializer };
|
||||
current.beginClass.parameterList = { creator, finalizeFlag };
|
||||
current.completeComponent.parameterList = { creator, finalizeFlag };
|
||||
current.finalizeComponent.parameterList = { creator, finalizeFlag };
|
||||
|
||||
compileRequiredPropertiesBundle(current, type, m_typeResolver);
|
||||
|
||||
if (current.requiredPropertiesBundle) {
|
||||
QmltcVariable bundle{
|
||||
u"const %1&"_s.arg(current.requiredPropertiesBundle->name),
|
||||
u"requiredPropertiesBundle"_s,
|
||||
};
|
||||
current.externalCtor.parameterList = { engine, bundle, parent, initializer };
|
||||
} else {
|
||||
current.externalCtor.parameterList = { engine, parent, initializer };
|
||||
}
|
||||
} else {
|
||||
current.externalCtor.parameterList = { creator, engine, parent };
|
||||
current.init.parameterList = { creator, engine, ctxtdata };
|
||||
|
@ -327,18 +469,7 @@ void QmltcCompiler::compileType(
|
|||
// compilation stub:
|
||||
current.externalCtor.body << u"Q_UNUSED(engine)"_s;
|
||||
if (documentRoot || inlineComponent) {
|
||||
current.externalCtor.body << u"// document root:"_s;
|
||||
// if it's document root, we want to create our QQmltcObjectCreationBase
|
||||
// that would store all the created objects
|
||||
current.externalCtor.body << u"QQmltcObjectCreationBase<%1> objectHolder;"_s.arg(
|
||||
type->internalName());
|
||||
current.externalCtor.body
|
||||
<< u"QQmltcObjectCreationHelper creator = objectHolder.view();"_s;
|
||||
current.externalCtor.body << u"creator.set(0, this);"_s; // special case
|
||||
// now call init
|
||||
current.externalCtor.body << current.init.name
|
||||
+ u"(&creator, engine, QQmlContextData::get(engine->rootContext()), /* "
|
||||
u"endInit */ true, initializer);";
|
||||
compileRootExternalConstructorBody(current, type);
|
||||
} else {
|
||||
current.externalCtor.body << u"// not document root:"_s;
|
||||
// just call init, we don't do any setup here otherwise
|
||||
|
|
|
@ -115,6 +115,19 @@ struct QmltcPropertyInitializer {
|
|||
QList<QmltcMethod> propertySetters;
|
||||
};
|
||||
|
||||
// Represents a generated class that contains a bundle of values to
|
||||
// initialize the required properties of a type.
|
||||
//
|
||||
// This is generally intended to be available for the root component
|
||||
// of the document, where it will be used as a constructor argument to
|
||||
// force the user to provide initial values for the required
|
||||
// properties of the constructed type.
|
||||
struct QmltcRequiredPropertiesBundle {
|
||||
QString name;
|
||||
|
||||
QList<QmltcVariable> members;
|
||||
};
|
||||
|
||||
// Represents QML -> C++ compiled type
|
||||
struct QmltcType
|
||||
{
|
||||
|
@ -158,6 +171,8 @@ struct QmltcType
|
|||
// A proxy class that provides a restricted interface that only
|
||||
// allows setting the properties of the type.
|
||||
QmltcPropertyInitializer propertyInitializer{};
|
||||
|
||||
std::optional<QmltcRequiredPropertiesBundle> requiredPropertiesBundle{};
|
||||
};
|
||||
|
||||
// Represents whole QML program, compiled to C++
|
||||
|
|
Loading…
Reference in New Issue