QQmlComponent/QQmlIncubator: Support C++ based types

QQmlComponent has a create overload which takes an Incubator. While
there is not really any work that could be done in an asynchronous with
a C++ based types (there are no sub-QML components, and there are no
bindings to set up), we still have to accommodate them in that overload
(otherwise we'd crash due to the missing compilation unit).

Add support in QQmlIncubator for this case, and call the newly introduce
function in QQmlComponent::create.

Task-number: QTBUG-97156
Change-Id: I64e3c6958117920dca214eee633424de94b492db
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
Fabian Kosmale 2022-11-22 18:09:49 +01:00
parent e2fcd32004
commit 9b3813be28
4 changed files with 81 additions and 2 deletions

View File

@ -1406,6 +1406,14 @@ void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context, QQmlC
incubator.clear();
QExplicitlySharedDataPointer<QQmlIncubatorPrivate> p(incubator.d);
if (d->loadedType.isValid()) {
// there isn't really an incubation process for C++ backed types
// so just create the object and signal that we are ready
p->incubateCppBasedComponent(this, context);
return;
}
QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(d->engine);
p->compilationUnit = d->compilationUnit;

View File

@ -103,6 +103,9 @@ QQmlIncubatorPrivate::~QQmlIncubatorPrivate()
void QQmlIncubatorPrivate::clear()
{
// reset the tagged pointer
if (requiredPropertiesFromComponent)
requiredPropertiesFromComponent = decltype(requiredPropertiesFromComponent){};
compilationUnit.reset();
if (next.isInList()) {
next.remove();
@ -373,6 +376,35 @@ finishIncubate:
}
}
/*!
\internal
This is used to mimic the behavior of incubate when the
Component we want to incubate refers to a creatable
QQmlType (i.e., it is the result of loadFromModule).
*/
void QQmlIncubatorPrivate::incubateCppBasedComponent(QQmlComponent *component, QQmlContext *context)
{
auto compPriv = QQmlComponentPrivate::get(component);
Q_ASSERT(compPriv->loadedType.isCreatable());
std::unique_ptr<QObject> object(component->beginCreate(context));
component->setInitialProperties(object.get(), initialProperties);
if (auto props = compPriv->state.requiredProperties()) {
requiredPropertiesFromComponent = props;
requiredPropertiesFromComponent.setTag(HadTopLevelRequired::Yes);
}
q->setInitialState(object.get());
if (requiredPropertiesFromComponent && !requiredPropertiesFromComponent->isEmpty()) {
for (auto unsetRequiredProperty: std::as_const(*requiredPropertiesFromComponent))
errors << QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(unsetRequiredProperty);
} else {
compPriv->completeCreate();
result = object.release();
progress = QQmlIncubatorPrivate::Completed;
}
changeStatus(calculateStatus());
}
/*!
Incubate objects for \a msecs, or until there are no more objects to incubate.
*/
@ -672,12 +704,18 @@ setInitialProperties can mark properties set there as no longer required.
*/
RequiredProperties *QQmlIncubatorPrivate::requiredProperties()
{
return creator->requiredProperties();
if (creator)
return creator->requiredProperties();
else
return requiredPropertiesFromComponent.data();
}
bool QQmlIncubatorPrivate::hadTopLevelRequiredProperties() const
{
return creator->componentHadTopLevelRequiredProperties();
if (creator)
return creator->componentHadTopLevelRequiredProperties();
else
return requiredPropertiesFromComponent.tag() == HadTopLevelRequired::Yes;
}
/*!

View File

@ -52,6 +52,14 @@ public:
QPointer<QObject> result;
enum HadTopLevelRequired : bool {No = 0, Yes = 1};
/* TODO: unify with Creator pointer once QTBUG-108760 is implemented
though we don't acutally own the properties here; if we ever end up
with a use case for async incubation of C++ types, we however could
not rely on the component to still exist during incubation, and
would need to store a copy of the required properties instead
*/
QTaggedPointer<RequiredProperties, HadTopLevelRequired> requiredPropertiesFromComponent;
QQmlGuardedContextData rootContext;
QQmlEnginePrivate *enginePriv;
QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
@ -70,6 +78,7 @@ public:
void forceCompletion(QQmlInstantiationInterrupt &i);
void incubate(QQmlInstantiationInterrupt &i);
void incubateCppBasedComponent(QQmlComponent *component, QQmlContext *context);
RequiredProperties *requiredProperties();
bool hadTopLevelRequiredProperties() const;
};

View File

@ -138,6 +138,7 @@ private slots:
void boundComponent();
void loadFromModule_data();
void loadFromModule();
void loadFromModuleThenCreateWithIncubator();
void loadFromModuleFailures_data();
void loadFromModuleFailures();
void loadFromModuleRequired();
@ -1328,6 +1329,29 @@ void tst_qqmlcomponent::loadFromModule()
name);
}
struct CallVerifyingIncubtor : QQmlIncubator
{
void setInitialState(QObject *) { setInitialStateCalled = true; }
void statusChanged(QQmlIncubator::Status status) { lastStatus = status; }
QQmlIncubator::Status lastStatus = QQmlIncubator::Null;
bool setInitialStateCalled = false;
};
void tst_qqmlcomponent::loadFromModuleThenCreateWithIncubator()
{
QQmlEngine engine;
QQmlComponent comp(&engine);
comp.loadFromModule("QtQuick", "Rectangle");
CallVerifyingIncubtor incubator;
comp.create(incubator);
std::unique_ptr<QObject> object { incubator.object() };
QVERIFY(incubator.setInitialStateCalled);
QVERIFY(incubator.isReady());
QCOMPARE(incubator.lastStatus, QQmlIncubator::Ready);
QCOMPARE(object->metaObject()->className(), "QQuickRectangle");
}
void tst_qqmlcomponent::loadFromModuleFailures_data()
{