1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtQml module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qqmlcomponent.h" |
41 | #include "qqmlcomponent_p.h" |
42 | #include "qqmlcomponentattached_p.h" |
43 | |
44 | #include "qqmlcontext_p.h" |
45 | #include "qqmlengine_p.h" |
46 | #include "qqmlvme_p.h" |
47 | #include "qqml.h" |
48 | #include "qqmlengine.h" |
49 | #include "qqmlbinding_p.h" |
50 | #include "qqmlincubator.h" |
51 | #include "qqmlincubator_p.h" |
52 | #include <private/qqmljavascriptexpression_p.h> |
53 | #include <private/qqmlsourcecoordinate_p.h> |
54 | |
55 | #include <private/qv4functionobject_p.h> |
56 | #include <private/qv4script_p.h> |
57 | #include <private/qv4scopedvalue_p.h> |
58 | #include <private/qv4objectiterator_p.h> |
59 | #include <private/qv4qobjectwrapper_p.h> |
60 | #include <private/qv4jscall_p.h> |
61 | |
62 | #include <QDir> |
63 | #include <QStack> |
64 | #include <QStringList> |
65 | #include <QThreadStorage> |
66 | #include <QtCore/qdebug.h> |
67 | #include <qqmlinfo.h> |
68 | |
69 | namespace { |
70 | QThreadStorage<int> creationDepth; |
71 | } |
72 | |
73 | QT_BEGIN_NAMESPACE |
74 | |
75 | class QQmlComponentExtension : public QV4::ExecutionEngine::Deletable |
76 | { |
77 | public: |
78 | QQmlComponentExtension(QV4::ExecutionEngine *v4); |
79 | virtual ~QQmlComponentExtension(); |
80 | |
81 | QV4::PersistentValue incubationProto; |
82 | }; |
83 | V4_DEFINE_EXTENSION(QQmlComponentExtension, componentExtension); |
84 | |
85 | /*! |
86 | \class QQmlComponent |
87 | \since 5.0 |
88 | \inmodule QtQml |
89 | |
90 | \brief The QQmlComponent class encapsulates a QML component definition. |
91 | |
92 | Components are reusable, encapsulated QML types with well-defined interfaces. |
93 | |
94 | A QQmlComponent instance can be created from a QML file. |
95 | For example, if there is a \c main.qml file like this: |
96 | |
97 | \qml |
98 | import QtQuick 2.0 |
99 | |
100 | Item { |
101 | width: 200 |
102 | height: 200 |
103 | } |
104 | \endqml |
105 | |
106 | The following code loads this QML file as a component, creates an instance of |
107 | this component using create(), and then queries the \l Item's \l {Item::}{width} |
108 | value: |
109 | |
110 | \code |
111 | QQmlEngine *engine = new QQmlEngine; |
112 | QQmlComponent component(engine, QUrl::fromLocalFile("main.qml")); |
113 | |
114 | QObject *myObject = component.create(); |
115 | QQuickItem *item = qobject_cast<QQuickItem*>(myObject); |
116 | int width = item->width(); // width = 200 |
117 | \endcode |
118 | |
119 | To create instances of a component in code where a QQmlEngine instance is |
120 | not available, you can use \l qmlContext() or \l qmlEngine(). For example, |
121 | in the scenario below, child items are being created within a QQuickItem |
122 | subclass: |
123 | |
124 | \code |
125 | void MyCppItem::init() |
126 | { |
127 | QQmlEngine *engine = qmlEngine(this); |
128 | // Or: |
129 | // QQmlEngine *engine = qmlContext(this)->engine(); |
130 | QQmlComponent component(engine, QUrl::fromLocalFile("MyItem.qml")); |
131 | QQuickItem *childItem = qobject_cast<QQuickItem*>(component.create()); |
132 | childItem->setParentItem(this); |
133 | } |
134 | \endcode |
135 | |
136 | Note that these functions will return \c null when called inside the |
137 | constructor of a QObject subclass, as the instance will not yet have |
138 | a context nor engine. |
139 | |
140 | \section2 Network Components |
141 | |
142 | If the URL passed to QQmlComponent is a network resource, or if the QML document references a |
143 | network resource, the QQmlComponent has to fetch the network data before it is able to create |
144 | objects. In this case, the QQmlComponent will have a \l {QQmlComponent::Loading}{Loading} |
145 | \l {QQmlComponent::status()}{status}. An application will have to wait until the component |
146 | is \l {QQmlComponent::Ready}{Ready} before calling \l {QQmlComponent::create()}. |
147 | |
148 | The following example shows how to load a QML file from a network resource. After creating |
149 | the QQmlComponent, it tests whether the component is loading. If it is, it connects to the |
150 | QQmlComponent::statusChanged() signal and otherwise calls the \c {continueLoading()} method |
151 | directly. Note that QQmlComponent::isLoading() may be false for a network component if the |
152 | component has been cached and is ready immediately. |
153 | |
154 | \code |
155 | MyApplication::MyApplication() |
156 | { |
157 | // ... |
158 | component = new QQmlComponent(engine, QUrl("http://www.example.com/main.qml")); |
159 | if (component->isLoading()) |
160 | QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)), |
161 | this, SLOT(continueLoading())); |
162 | else |
163 | continueLoading(); |
164 | } |
165 | |
166 | void MyApplication::continueLoading() |
167 | { |
168 | if (component->isError()) { |
169 | qWarning() << component->errors(); |
170 | } else { |
171 | QObject *myObject = component->create(); |
172 | } |
173 | } |
174 | \endcode |
175 | */ |
176 | |
177 | /*! |
178 | \qmltype Component |
179 | \instantiates QQmlComponent |
180 | \ingroup qml-utility-elements |
181 | \inqmlmodule QtQml |
182 | \brief Encapsulates a QML component definition. |
183 | |
184 | Components are reusable, encapsulated QML types with well-defined interfaces. |
185 | |
186 | Components are often defined by \l {{QML Documents}}{component files} - |
187 | that is, \c .qml files. The \e Component type essentially allows QML components |
188 | to be defined inline, within a \l {QML Documents}{QML document}, rather than as a separate QML file. |
189 | This may be useful for reusing a small component within a QML file, or for defining |
190 | a component that logically belongs with other QML components within a file. |
191 | |
192 | For example, here is a component that is used by multiple \l Loader objects. |
193 | It contains a single item, a \l Rectangle: |
194 | |
195 | \snippet qml/component.qml 0 |
196 | |
197 | Notice that while a \l Rectangle by itself would be automatically |
198 | rendered and displayed, this is not the case for the above rectangle |
199 | because it is defined inside a \c Component. The component encapsulates the |
200 | QML types within, as if they were defined in a separate QML |
201 | file, and is not loaded until requested (in this case, by the |
202 | two \l Loader objects). Because Component is not derived from Item, you cannot |
203 | anchor anything to it. |
204 | |
205 | Defining a \c Component is similar to defining a \l {QML Documents}{QML document}. |
206 | A QML document has a single top-level item that defines the behavior and |
207 | properties of that component, and cannot define properties or behavior outside |
208 | of that top-level item. In the same way, a \c Component definition contains a single |
209 | top level item (which in the above example is a \l Rectangle) and cannot define any |
210 | data outside of this item, with the exception of an \e id (which in the above example |
211 | is \e redSquare). |
212 | |
213 | The \c Component type is commonly used to provide graphical components |
214 | for views. For example, the ListView::delegate property requires a \c Component |
215 | to specify how each list item is to be displayed. |
216 | |
217 | \c Component objects can also be created dynamically using |
218 | \l{QtQml::Qt::createComponent()}{Qt.createComponent()}. |
219 | |
220 | \section2 Creation Context |
221 | |
222 | The creation context of a Component corresponds to the context where the Component was declared. |
223 | This context is used as the parent context (creating a \l{qtqml-documents-scope.html#component-instance-hierarchy}{context hierarchy}) |
224 | when the component is instantiated by an object such as a ListView or a Loader. |
225 | |
226 | In the following example, \c comp1 is created within the root context of MyItem.qml, and any objects |
227 | instantiated from this component will have access to the ids and properties within that context, |
228 | such as \c internalSettings.color. When \c comp1 is used as a ListView delegate in another context |
229 | (as in main.qml below), it will continue to have access to the properties of its creation context |
230 | (which would otherwise be private to external users). |
231 | |
232 | \table |
233 | \row |
234 | \li MyItem.qml |
235 | \li \snippet qml/component/MyItem.qml 0 |
236 | \row |
237 | \li main.qml |
238 | \li \snippet qml/component/main.qml 0 |
239 | \endtable |
240 | |
241 | It is important that the lifetime of the creation context outlive any created objects. See |
242 | \l{Maintaining Dynamically Created Objects} for more details. |
243 | */ |
244 | |
245 | /*! |
246 | \qmlattachedsignal Component::completed() |
247 | |
248 | Emitted after the object has been instantiated. This can be used to |
249 | execute script code at startup, once the full QML environment has been |
250 | established. |
251 | |
252 | The \c onCompleted signal handler can be declared on any object. The order |
253 | of running the handlers is undefined. |
254 | |
255 | \qml |
256 | Rectangle { |
257 | Component.onCompleted: console.log("Completed Running!") |
258 | Rectangle { |
259 | Component.onCompleted: console.log("Nested Completed Running!") |
260 | } |
261 | } |
262 | \endqml |
263 | */ |
264 | |
265 | /*! |
266 | \qmlattachedsignal Component::destruction() |
267 | |
268 | Emitted as the object begins destruction. This can be used to undo |
269 | work done in response to the \l {completed}{completed()} signal, or other |
270 | imperative code in your application. |
271 | |
272 | The \c onDestruction signal handler can be declared on any object. The |
273 | order of running the handlers is undefined. |
274 | |
275 | \qml |
276 | Rectangle { |
277 | Component.onDestruction: console.log("Destruction Beginning!") |
278 | Rectangle { |
279 | Component.onDestruction: console.log("Nested Destruction Beginning!") |
280 | } |
281 | } |
282 | \endqml |
283 | |
284 | \sa {Qt QML} |
285 | */ |
286 | |
287 | /*! |
288 | \enum QQmlComponent::Status |
289 | |
290 | Specifies the loading status of the QQmlComponent. |
291 | |
292 | \value Null This QQmlComponent has no data. Call loadUrl() or setData() to add QML content. |
293 | \value Ready This QQmlComponent is ready and create() may be called. |
294 | \value Loading This QQmlComponent is loading network data. |
295 | \value Error An error has occurred. Call errors() to retrieve a list of \l {QQmlError}{errors}. |
296 | */ |
297 | |
298 | /*! |
299 | \enum QQmlComponent::CompilationMode |
300 | |
301 | Specifies whether the QQmlComponent should load the component immediately, or asynchonously. |
302 | |
303 | \value PreferSynchronous Prefer loading/compiling the component immediately, blocking the thread. |
304 | This is not always possible; for example, remote URLs will always load asynchronously. |
305 | \value Asynchronous Load/compile the component in a background thread. |
306 | */ |
307 | |
308 | void QQmlComponentPrivate::typeDataReady(QQmlTypeData *) |
309 | { |
310 | Q_Q(QQmlComponent); |
311 | |
312 | Q_ASSERT(typeData); |
313 | |
314 | fromTypeData(data: typeData); |
315 | typeData = nullptr; |
316 | progress = 1.0; |
317 | |
318 | emit q->statusChanged(q->status()); |
319 | emit q->progressChanged(progress); |
320 | } |
321 | |
322 | void QQmlComponentPrivate::typeDataProgress(QQmlTypeData *, qreal p) |
323 | { |
324 | Q_Q(QQmlComponent); |
325 | |
326 | progress = p; |
327 | |
328 | emit q->progressChanged(p); |
329 | } |
330 | |
331 | void QQmlComponentPrivate::fromTypeData(const QQmlRefPointer<QQmlTypeData> &data) |
332 | { |
333 | url = data->finalUrl(); |
334 | compilationUnit = data->compilationUnit(); |
335 | |
336 | if (!compilationUnit) { |
337 | Q_ASSERT(data->isError()); |
338 | state.errors = data->errors(); |
339 | } |
340 | } |
341 | |
342 | RequiredProperties &QQmlComponentPrivate::requiredProperties() |
343 | { |
344 | return state.creator->requiredProperties(); |
345 | } |
346 | |
347 | bool QQmlComponentPrivate::hadRequiredProperties() const |
348 | { |
349 | return state.creator->componentHadRequiredProperties(); |
350 | } |
351 | |
352 | void QQmlComponentPrivate::clear() |
353 | { |
354 | if (typeData) { |
355 | typeData->unregisterCallback(this); |
356 | typeData = nullptr; |
357 | } |
358 | |
359 | compilationUnit = nullptr; |
360 | } |
361 | |
362 | QObject *QQmlComponentPrivate::doBeginCreate(QQmlComponent *q, QQmlContext *context) |
363 | { |
364 | if (!engine) { |
365 | // ###Qt6: In Qt 6, it should be impossible for users to create a QQmlComponent without an engine, and we can remove this check |
366 | qWarning(msg: "QQmlComponent: Must provide an engine before calling create" ); |
367 | return nullptr; |
368 | } |
369 | if (!context) |
370 | context = engine->rootContext(); |
371 | return q->beginCreate(context); |
372 | } |
373 | |
374 | bool QQmlComponentPrivate::setInitialProperty(QObject *component, const QString& name, const QVariant &value) |
375 | { |
376 | QQmlProperty prop = QQmlComponentPrivate::removePropertyFromRequired(createdComponent: component, name, requiredProperties&: requiredProperties()); |
377 | QQmlPropertyPrivate *privProp = QQmlPropertyPrivate::get(p: prop); |
378 | if (!prop.isValid() || !privProp->writeValueProperty(value, {})) { |
379 | QQmlError error{}; |
380 | error.setUrl(url); |
381 | error.setDescription(QLatin1String("Could not set property %1" ).arg(args: name)); |
382 | state.errors.push_back(t: error); |
383 | return false; |
384 | } else |
385 | return true; |
386 | } |
387 | |
388 | /*! |
389 | \internal |
390 | */ |
391 | QQmlComponent::QQmlComponent(QObject *parent) |
392 | : QObject(*(new QQmlComponentPrivate), parent) |
393 | { |
394 | } |
395 | |
396 | /*! |
397 | Destruct the QQmlComponent. |
398 | */ |
399 | QQmlComponent::~QQmlComponent() |
400 | { |
401 | Q_D(QQmlComponent); |
402 | |
403 | if (d->state.completePending) { |
404 | qWarning(msg: "QQmlComponent: Component destroyed while completion pending" ); |
405 | |
406 | if (isError()) { |
407 | qWarning() << "This may have been caused by one of the following errors:" ; |
408 | for (const QQmlError &error : qAsConst(t&: d->state.errors)) |
409 | qWarning().nospace().noquote() << QLatin1String(" " ) << error; |
410 | } |
411 | |
412 | d->completeCreate(); |
413 | } |
414 | |
415 | if (d->typeData) { |
416 | d->typeData->unregisterCallback(d); |
417 | d->typeData = nullptr; |
418 | } |
419 | } |
420 | |
421 | /*! |
422 | \qmlproperty enumeration Component::status |
423 | This property holds the status of component loading. The status can be one of the |
424 | following: |
425 | \list |
426 | \li Component.Null - no data is available for the component |
427 | \li Component.Ready - the component has been loaded, and can be used to create instances. |
428 | \li Component.Loading - the component is currently being loaded |
429 | \li Component.Error - an error occurred while loading the component. |
430 | Calling errorString() will provide a human-readable description of any errors. |
431 | \endlist |
432 | */ |
433 | |
434 | /*! |
435 | \property QQmlComponent::status |
436 | The component's current \l{QQmlComponent::Status} {status}. |
437 | */ |
438 | QQmlComponent::Status QQmlComponent::status() const |
439 | { |
440 | Q_D(const QQmlComponent); |
441 | |
442 | if (d->typeData) |
443 | return Loading; |
444 | else if (!d->state.errors.isEmpty()) |
445 | return Error; |
446 | else if (d->engine && d->compilationUnit) |
447 | return Ready; |
448 | else |
449 | return Null; |
450 | } |
451 | |
452 | /*! |
453 | Returns true if status() == QQmlComponent::Null. |
454 | */ |
455 | bool QQmlComponent::isNull() const |
456 | { |
457 | return status() == Null; |
458 | } |
459 | |
460 | /*! |
461 | Returns true if status() == QQmlComponent::Ready. |
462 | */ |
463 | bool QQmlComponent::isReady() const |
464 | { |
465 | return status() == Ready; |
466 | } |
467 | |
468 | /*! |
469 | Returns true if status() == QQmlComponent::Error. |
470 | */ |
471 | bool QQmlComponent::isError() const |
472 | { |
473 | return status() == Error; |
474 | } |
475 | |
476 | /*! |
477 | Returns true if status() == QQmlComponent::Loading. |
478 | */ |
479 | bool QQmlComponent::isLoading() const |
480 | { |
481 | return status() == Loading; |
482 | } |
483 | |
484 | /*! |
485 | \qmlproperty real Component::progress |
486 | The progress of loading the component, from 0.0 (nothing loaded) |
487 | to 1.0 (finished). |
488 | */ |
489 | |
490 | /*! |
491 | \property QQmlComponent::progress |
492 | The progress of loading the component, from 0.0 (nothing loaded) |
493 | to 1.0 (finished). |
494 | */ |
495 | qreal QQmlComponent::progress() const |
496 | { |
497 | Q_D(const QQmlComponent); |
498 | return d->progress; |
499 | } |
500 | |
501 | /*! |
502 | \fn void QQmlComponent::progressChanged(qreal progress) |
503 | |
504 | Emitted whenever the component's loading progress changes. \a progress will be the |
505 | current progress between 0.0 (nothing loaded) and 1.0 (finished). |
506 | */ |
507 | |
508 | /*! |
509 | \fn void QQmlComponent::statusChanged(QQmlComponent::Status status) |
510 | |
511 | Emitted whenever the component's status changes. \a status will be the |
512 | new status. |
513 | */ |
514 | |
515 | /*! |
516 | Create a QQmlComponent with no data and give it the specified |
517 | \a engine and \a parent. Set the data with setData(). |
518 | */ |
519 | QQmlComponent::QQmlComponent(QQmlEngine *engine, QObject *parent) |
520 | : QObject(*(new QQmlComponentPrivate), parent) |
521 | { |
522 | Q_D(QQmlComponent); |
523 | d->engine = engine; |
524 | } |
525 | |
526 | /*! |
527 | Create a QQmlComponent from the given \a url and give it the |
528 | specified \a parent and \a engine. |
529 | |
530 | \include qqmlcomponent.qdoc url-note |
531 | |
532 | \sa loadUrl() |
533 | */ |
534 | QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, QObject *parent) |
535 | : QQmlComponent(engine, url, QQmlComponent::PreferSynchronous, parent) |
536 | { |
537 | } |
538 | |
539 | /*! |
540 | Create a QQmlComponent from the given \a url and give it the |
541 | specified \a parent and \a engine. If \a mode is \l Asynchronous, |
542 | the component will be loaded and compiled asynchronously. |
543 | |
544 | \include qqmlcomponent.qdoc url-note |
545 | |
546 | \sa loadUrl() |
547 | */ |
548 | QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, CompilationMode mode, |
549 | QObject *parent) |
550 | : QQmlComponent(engine, parent) |
551 | { |
552 | Q_D(QQmlComponent); |
553 | d->loadUrl(newUrl: url, mode); |
554 | } |
555 | |
556 | /*! |
557 | Create a QQmlComponent from the given \a fileName and give it the specified |
558 | \a parent and \a engine. |
559 | |
560 | \sa loadUrl() |
561 | */ |
562 | QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName, |
563 | QObject *parent) |
564 | : QQmlComponent(engine, fileName, QQmlComponent::PreferSynchronous, parent) |
565 | { |
566 | } |
567 | |
568 | /*! |
569 | Create a QQmlComponent from the given \a fileName and give it the specified |
570 | \a parent and \a engine. If \a mode is \l Asynchronous, |
571 | the component will be loaded and compiled asynchronously. |
572 | |
573 | \sa loadUrl() |
574 | */ |
575 | QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName, |
576 | CompilationMode mode, QObject *parent) |
577 | : QQmlComponent(engine, parent) |
578 | { |
579 | Q_D(QQmlComponent); |
580 | const QUrl url = QDir::isAbsolutePath(path: fileName) ? QUrl::fromLocalFile(localfile: fileName) : QUrl(fileName); |
581 | d->loadUrl(newUrl: url, mode); |
582 | } |
583 | |
584 | /*! |
585 | \internal |
586 | */ |
587 | QQmlComponent::QQmlComponent(QQmlEngine *engine, QV4::ExecutableCompilationUnit *compilationUnit, |
588 | int start, QObject *parent) |
589 | : QQmlComponent(engine, parent) |
590 | { |
591 | Q_D(QQmlComponent); |
592 | d->compilationUnit = compilationUnit; |
593 | d->start = start; |
594 | d->url = compilationUnit->finalUrl(); |
595 | d->progress = 1.0; |
596 | } |
597 | |
598 | /*! |
599 | Sets the QQmlComponent to use the given QML \a data. If \a url |
600 | is provided, it is used to set the component name and to provide |
601 | a base path for items resolved by this component. |
602 | */ |
603 | void QQmlComponent::setData(const QByteArray &data, const QUrl &url) |
604 | { |
605 | Q_D(QQmlComponent); |
606 | |
607 | if (!d->engine) { |
608 | // ###Qt6: In Qt 6, it should be impossible for users to create a QQmlComponent without an engine, and we can remove this check |
609 | qWarning(msg: "QQmlComponent: Must provide an engine before calling setData" ); |
610 | return; |
611 | } |
612 | |
613 | d->clear(); |
614 | |
615 | d->url = url; |
616 | |
617 | QQmlRefPointer<QQmlTypeData> typeData = QQmlEnginePrivate::get(e: d->engine)->typeLoader.getType(data, url); |
618 | |
619 | if (typeData->isCompleteOrError()) { |
620 | d->fromTypeData(data: typeData); |
621 | } else { |
622 | d->typeData = typeData; |
623 | d->typeData->registerCallback(d); |
624 | } |
625 | |
626 | d->progress = 1.0; |
627 | emit statusChanged(status()); |
628 | emit progressChanged(d->progress); |
629 | } |
630 | |
631 | /*! |
632 | Returns the QQmlContext the component was created in. This is only |
633 | valid for components created directly from QML. |
634 | */ |
635 | QQmlContext *QQmlComponent::creationContext() const |
636 | { |
637 | Q_D(const QQmlComponent); |
638 | if(d->creationContext) |
639 | return d->creationContext->asQQmlContext(); |
640 | |
641 | return qmlContext(this); |
642 | } |
643 | |
644 | /*! |
645 | Returns the QQmlEngine of this component. |
646 | |
647 | \since 5.12 |
648 | */ |
649 | QQmlEngine *QQmlComponent::engine() const |
650 | { |
651 | Q_D(const QQmlComponent); |
652 | return d->engine; |
653 | } |
654 | |
655 | /*! |
656 | Load the QQmlComponent from the provided \a url. |
657 | |
658 | \include qqmlcomponent.qdoc url-note |
659 | */ |
660 | void QQmlComponent::loadUrl(const QUrl &url) |
661 | { |
662 | Q_D(QQmlComponent); |
663 | d->loadUrl(newUrl: url); |
664 | } |
665 | |
666 | /*! |
667 | Load the QQmlComponent from the provided \a url. |
668 | If \a mode is \l Asynchronous, the component will be loaded and compiled asynchronously. |
669 | |
670 | \include qqmlcomponent.qdoc url-note |
671 | */ |
672 | void QQmlComponent::loadUrl(const QUrl &url, QQmlComponent::CompilationMode mode) |
673 | { |
674 | Q_D(QQmlComponent); |
675 | d->loadUrl(newUrl: url, mode); |
676 | } |
677 | |
678 | void QQmlComponentPrivate::loadUrl(const QUrl &newUrl, QQmlComponent::CompilationMode mode) |
679 | { |
680 | Q_Q(QQmlComponent); |
681 | clear(); |
682 | |
683 | if (newUrl.isRelative()) { |
684 | // The new URL is a relative URL like QUrl("main.qml"). |
685 | url = engine->baseUrl().resolved(relative: QUrl(newUrl.toString())); |
686 | } else if (engine->baseUrl().isLocalFile() && newUrl.isLocalFile() && !QDir::isAbsolutePath(path: newUrl.toLocalFile())) { |
687 | // The new URL is a file on disk but it's a relative path; e.g.: |
688 | // QUrl::fromLocalFile("main.qml") or QUrl("file:main.qml") |
689 | // We need to remove the scheme so that it becomes a relative URL with a relative path: |
690 | QUrl fixedUrl(newUrl); |
691 | fixedUrl.setScheme(QString()); |
692 | // Then, turn it into an absolute URL with an absolute path by resolving it against the engine's baseUrl(). |
693 | // This is a compatibility hack for QTBUG-58837. |
694 | url = engine->baseUrl().resolved(relative: fixedUrl); |
695 | } else { |
696 | url = newUrl; |
697 | } |
698 | |
699 | if (newUrl.isEmpty()) { |
700 | QQmlError error; |
701 | error.setDescription(QQmlComponent::tr(s: "Invalid empty URL" )); |
702 | state.errors << error; |
703 | return; |
704 | } |
705 | |
706 | if (progress != 0.0) { |
707 | progress = 0.0; |
708 | emit q->progressChanged(progress); |
709 | } |
710 | |
711 | QQmlTypeLoader::Mode loaderMode = (mode == QQmlComponent::Asynchronous) |
712 | ? QQmlTypeLoader::Asynchronous |
713 | : QQmlTypeLoader::PreferSynchronous; |
714 | |
715 | QQmlRefPointer<QQmlTypeData> data = QQmlEnginePrivate::get(e: engine)->typeLoader.getType(unNormalizedUrl: url, mode: loaderMode); |
716 | |
717 | if (data->isCompleteOrError()) { |
718 | fromTypeData(data); |
719 | progress = 1.0; |
720 | } else { |
721 | typeData = data; |
722 | typeData->registerCallback(this); |
723 | progress = data->progress(); |
724 | } |
725 | |
726 | emit q->statusChanged(q->status()); |
727 | if (progress != 0.0) |
728 | emit q->progressChanged(progress); |
729 | } |
730 | |
731 | /*! |
732 | Returns the list of errors that occurred during the last compile or create |
733 | operation. An empty list is returned if isError() is not set. |
734 | */ |
735 | QList<QQmlError> QQmlComponent::errors() const |
736 | { |
737 | Q_D(const QQmlComponent); |
738 | if (isError()) |
739 | return d->state.errors; |
740 | else |
741 | return QList<QQmlError>(); |
742 | } |
743 | |
744 | /*! |
745 | \qmlmethod string Component::errorString() |
746 | |
747 | Returns a human-readable description of any error. |
748 | |
749 | The string includes the file, location, and description of each error. |
750 | If multiple errors are present, they are separated by a newline character. |
751 | |
752 | If no errors are present, an empty string is returned. |
753 | */ |
754 | |
755 | /*! |
756 | \internal |
757 | errorString is only meant as a way to get the errors in script |
758 | */ |
759 | QString QQmlComponent::errorString() const |
760 | { |
761 | Q_D(const QQmlComponent); |
762 | QString ret; |
763 | if(!isError()) |
764 | return ret; |
765 | for (const QQmlError &e : d->state.errors) { |
766 | ret += e.url().toString() + QLatin1Char(':') + |
767 | QString::number(e.line()) + QLatin1Char(' ') + |
768 | e.description() + QLatin1Char('\n'); |
769 | } |
770 | return ret; |
771 | } |
772 | |
773 | /*! |
774 | \qmlproperty url Component::url |
775 | The component URL. This is the URL that was used to construct the component. |
776 | */ |
777 | |
778 | /*! |
779 | \property QQmlComponent::url |
780 | The component URL. This is the URL passed to either the constructor, |
781 | or the loadUrl(), or setData() methods. |
782 | */ |
783 | QUrl QQmlComponent::url() const |
784 | { |
785 | Q_D(const QQmlComponent); |
786 | return d->url; |
787 | } |
788 | |
789 | /*! |
790 | \internal |
791 | */ |
792 | QQmlComponent::QQmlComponent(QQmlComponentPrivate &dd, QObject *parent) |
793 | : QObject(dd, parent) |
794 | { |
795 | } |
796 | |
797 | /*! |
798 | Create an object instance from this component. Returns \nullptr if creation |
799 | failed. \a context specifies the context within which to create the object |
800 | instance. |
801 | |
802 | If \a context is \nullptr (the default), it will create the instance in the |
803 | \l {QQmlEngine::rootContext()}{root context} of the engine. |
804 | |
805 | The ownership of the returned object instance is transferred to the caller. |
806 | |
807 | If the object being created from this component is a visual item, it must |
808 | have a visual parent, which can be set by calling |
809 | QQuickItem::setParentItem(). See \l {Concepts - Visual Parent in Qt Quick} |
810 | for more details. |
811 | |
812 | \sa QQmlEngine::ObjectOwnership |
813 | */ |
814 | QObject *QQmlComponent::create(QQmlContext *context) |
815 | { |
816 | Q_D(QQmlComponent); |
817 | |
818 | QObject *rv = d->doBeginCreate(q: this, context); |
819 | if (rv) |
820 | completeCreate(); |
821 | if (rv && !d->requiredProperties().empty()) { |
822 | delete rv; |
823 | return nullptr; |
824 | } |
825 | return rv; |
826 | } |
827 | |
828 | /*! |
829 | Create an object instance of this component, and initialize its toplevel |
830 | properties with \a initialProperties. \a context specifies the context |
831 | where the object instance is to be created. |
832 | |
833 | \sa QQmlComponent::create |
834 | \since 5.14 |
835 | */ |
836 | QObject *QQmlComponent::createWithInitialProperties(const QVariantMap& initialProperties, QQmlContext *context) |
837 | { |
838 | Q_D(QQmlComponent); |
839 | |
840 | QObject *rv = d->doBeginCreate(q: this, context); |
841 | if (rv) { |
842 | setInitialProperties(component: rv, properties: initialProperties); |
843 | completeCreate(); |
844 | } |
845 | if (!d->requiredProperties().empty()) { |
846 | d->requiredProperties().clear(); |
847 | return nullptr; |
848 | } |
849 | return rv; |
850 | } |
851 | |
852 | /*! |
853 | This method provides advanced control over component instance creation. |
854 | In general, programmers should use QQmlComponent::create() to create object |
855 | instances. |
856 | |
857 | Create an object instance from this component. Returns \nullptr if creation |
858 | failed. \a publicContext specifies the context within which to create the object |
859 | instance. |
860 | |
861 | When QQmlComponent constructs an instance, it occurs in three steps: |
862 | \list 1 |
863 | \li The object hierarchy is created, and constant values are assigned. |
864 | \li Property bindings are evaluated for the first time. |
865 | \li If applicable, QQmlParserStatus::componentComplete() is called on objects. |
866 | \endlist |
867 | QQmlComponent::beginCreate() differs from QQmlComponent::create() in that it |
868 | only performs step 1. QQmlComponent::completeCreate() must be called to |
869 | complete steps 2 and 3. |
870 | |
871 | This breaking point is sometimes useful when using attached properties to |
872 | communicate information to an instantiated component, as it allows their |
873 | initial values to be configured before property bindings take effect. |
874 | |
875 | The ownership of the returned object instance is transferred to the caller. |
876 | |
877 | \sa completeCreate(), QQmlEngine::ObjectOwnership |
878 | */ |
879 | QObject *QQmlComponent::beginCreate(QQmlContext *publicContext) |
880 | { |
881 | Q_D(QQmlComponent); |
882 | |
883 | Q_ASSERT(publicContext); |
884 | QQmlContextData *context = QQmlContextData::get(context: publicContext); |
885 | |
886 | return d->beginCreate(context); |
887 | } |
888 | |
889 | QObject * |
890 | QQmlComponentPrivate::beginCreate(QQmlContextData *context) |
891 | { |
892 | Q_Q(QQmlComponent); |
893 | if (!context) { |
894 | qWarning(msg: "QQmlComponent: Cannot create a component in a null context" ); |
895 | return nullptr; |
896 | } |
897 | |
898 | if (!context->isValid()) { |
899 | qWarning(msg: "QQmlComponent: Cannot create a component in an invalid context" ); |
900 | return nullptr; |
901 | } |
902 | |
903 | if (context->engine != engine) { |
904 | qWarning(msg: "QQmlComponent: Must create component in context from the same QQmlEngine" ); |
905 | return nullptr; |
906 | } |
907 | |
908 | if (state.completePending) { |
909 | qWarning(msg: "QQmlComponent: Cannot create new component instance before completing the previous" ); |
910 | return nullptr; |
911 | } |
912 | |
913 | if (!q->isReady()) { |
914 | qWarning(msg: "QQmlComponent: Component is not ready" ); |
915 | return nullptr; |
916 | } |
917 | |
918 | // Do not create infinite recursion in object creation |
919 | static const int maxCreationDepth = 10; |
920 | if (creationDepth.localData() >= maxCreationDepth) { |
921 | qWarning(msg: "QQmlComponent: Component creation is recursing - aborting" ); |
922 | return nullptr; |
923 | } |
924 | |
925 | QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(e: engine); |
926 | |
927 | enginePriv->inProgressCreations++; |
928 | state.errors.clear(); |
929 | state.completePending = true; |
930 | |
931 | enginePriv->referenceScarceResources(); |
932 | QObject *rv = nullptr; |
933 | state.creator.reset(other: new QQmlObjectCreator(context, compilationUnit, creationContext)); |
934 | rv = state.creator->create(subComponentIndex: start); |
935 | if (!rv) |
936 | state.errors = state.creator->errors; |
937 | enginePriv->dereferenceScarceResources(); |
938 | |
939 | if (rv) { |
940 | QQmlData *ddata = QQmlData::get(object: rv); |
941 | Q_ASSERT(ddata); |
942 | //top level objects should never get JS ownership. |
943 | //if JS ownership is needed this needs to be explicitly undone (like in component.createObject()) |
944 | ddata->indestructible = true; |
945 | ddata->explicitIndestructibleSet = true; |
946 | ddata->rootObjectInCreation = false; |
947 | } |
948 | |
949 | return rv; |
950 | } |
951 | |
952 | void QQmlComponentPrivate::beginDeferred(QQmlEnginePrivate *enginePriv, |
953 | QObject *object, DeferredState *deferredState) |
954 | { |
955 | QQmlData *ddata = QQmlData::get(object); |
956 | Q_ASSERT(!ddata->deferredData.isEmpty()); |
957 | |
958 | deferredState->constructionStates.reserve(size: ddata->deferredData.size()); |
959 | |
960 | for (QQmlData::DeferredData *deferredData : qAsConst(t&: ddata->deferredData)) { |
961 | enginePriv->inProgressCreations++; |
962 | |
963 | ConstructionState *state = new ConstructionState; |
964 | state->completePending = true; |
965 | |
966 | QQmlContextData *creationContext = nullptr; |
967 | state->creator.reset(other: new QQmlObjectCreator(deferredData->context->parent, deferredData->compilationUnit, creationContext)); |
968 | |
969 | if (!state->creator->populateDeferredProperties(instance: object, deferredData)) |
970 | state->errors << state->creator->errors; |
971 | deferredData->bindings.clear(); |
972 | |
973 | deferredState->constructionStates += state; |
974 | } |
975 | } |
976 | |
977 | void QQmlComponentPrivate::completeDeferred(QQmlEnginePrivate *enginePriv, QQmlComponentPrivate::DeferredState *deferredState) |
978 | { |
979 | for (ConstructionState *state : qAsConst(t&: deferredState->constructionStates)) |
980 | complete(enginePriv, state); |
981 | } |
982 | |
983 | void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionState *state) |
984 | { |
985 | if (state->completePending) { |
986 | QQmlInstantiationInterrupt interrupt; |
987 | state->creator->finalize(interrupt); |
988 | |
989 | state->completePending = false; |
990 | |
991 | enginePriv->inProgressCreations--; |
992 | |
993 | if (0 == enginePriv->inProgressCreations) { |
994 | while (enginePriv->erroredBindings) { |
995 | enginePriv->warning(enginePriv->erroredBindings->removeError()); |
996 | } |
997 | } |
998 | } |
999 | } |
1000 | |
1001 | /*! |
1002 | * \internal |
1003 | * Finds the matching toplevel property with name \a name of the component \a createdComponent. |
1004 | * If it was a required property or an alias to a required property contained in \a |
1005 | * requiredProperties, it is removed from it. |
1006 | * |
1007 | * If wasInRequiredProperties is non-null, the referenced boolean is set to true iff the property |
1008 | * was found in requiredProperties. |
1009 | * |
1010 | * Returns the QQmlProperty with name \a name (which might be invalid if there is no such property), |
1011 | * for further processing (for instance, actually setting the property value). |
1012 | * |
1013 | * Note: This method is used in QQmlComponent and QQmlIncubator to manage required properties. Most |
1014 | * classes which create components should not need it and should only need to call |
1015 | * setInitialProperties. |
1016 | */ |
1017 | QQmlProperty QQmlComponentPrivate::removePropertyFromRequired(QObject *createdComponent, const QString &name, RequiredProperties &requiredProperties, bool* wasInRequiredProperties) |
1018 | { |
1019 | QQmlProperty prop(createdComponent, name); |
1020 | auto privProp = QQmlPropertyPrivate::get(p: prop); |
1021 | if (prop.isValid()) { |
1022 | // resolve outstanding required properties |
1023 | auto targetProp = &privProp->core; |
1024 | if (targetProp->isAlias()) { |
1025 | auto target = createdComponent; |
1026 | QQmlPropertyIndex originalIndex(targetProp->coreIndex()); |
1027 | QQmlPropertyIndex propIndex; |
1028 | QQmlPropertyPrivate::findAliasTarget(target, originalIndex, &target, &propIndex); |
1029 | QQmlData *data = QQmlData::get(object: target); |
1030 | Q_ASSERT(data && data->propertyCache); |
1031 | targetProp = data->propertyCache->property(index: propIndex.coreIndex()); |
1032 | } else { |
1033 | // we need to get the pointer from the property cache instead of directly using |
1034 | // targetProp else the lookup will fail |
1035 | QQmlData *data = QQmlData::get(object: createdComponent); |
1036 | Q_ASSERT(data && data->propertyCache); |
1037 | targetProp = data->propertyCache->property(index: targetProp->coreIndex()); |
1038 | } |
1039 | auto it = requiredProperties.find(key: targetProp); |
1040 | if (it != requiredProperties.end()) { |
1041 | if (wasInRequiredProperties) |
1042 | *wasInRequiredProperties = true; |
1043 | requiredProperties.erase(it); |
1044 | } else { |
1045 | if (wasInRequiredProperties) |
1046 | *wasInRequiredProperties = false; |
1047 | } |
1048 | } |
1049 | return prop; |
1050 | } |
1051 | |
1052 | /*! |
1053 | This method provides advanced control over component instance creation. |
1054 | In general, programmers should use QQmlComponent::create() to create a |
1055 | component. |
1056 | |
1057 | This function completes the component creation begun with QQmlComponent::beginCreate() |
1058 | and must be called afterwards. |
1059 | |
1060 | \sa beginCreate() |
1061 | */ |
1062 | void QQmlComponent::completeCreate() |
1063 | { |
1064 | Q_D(QQmlComponent); |
1065 | |
1066 | d->completeCreate(); |
1067 | } |
1068 | |
1069 | void QQmlComponentPrivate::completeCreate() |
1070 | { |
1071 | const RequiredProperties& unsetRequiredProperties = requiredProperties(); |
1072 | for (const auto& unsetRequiredProperty: unsetRequiredProperties) { |
1073 | QQmlError error = unsetRequiredPropertyToQQmlError(unsetRequiredProperty); |
1074 | state.errors.push_back(t: error); |
1075 | } |
1076 | if (state.completePending) { |
1077 | ++creationDepth.localData(); |
1078 | QQmlEnginePrivate *ep = QQmlEnginePrivate::get(e: engine); |
1079 | complete(enginePriv: ep, state: &state); |
1080 | --creationDepth.localData(); |
1081 | } |
1082 | } |
1083 | |
1084 | QQmlComponentAttached::QQmlComponentAttached(QObject *parent) |
1085 | : QObject(parent), prev(nullptr), next(nullptr) |
1086 | { |
1087 | } |
1088 | |
1089 | QQmlComponentAttached::~QQmlComponentAttached() |
1090 | { |
1091 | if (prev) *prev = next; |
1092 | if (next) next->prev = prev; |
1093 | prev = nullptr; |
1094 | next = nullptr; |
1095 | } |
1096 | |
1097 | /*! |
1098 | \internal |
1099 | */ |
1100 | QQmlComponentAttached *QQmlComponent::qmlAttachedProperties(QObject *obj) |
1101 | { |
1102 | QQmlComponentAttached *a = new QQmlComponentAttached(obj); |
1103 | |
1104 | QQmlEngine *engine = qmlEngine(obj); |
1105 | if (!engine) |
1106 | return a; |
1107 | |
1108 | QQmlEnginePrivate *p = QQmlEnginePrivate::get(e: engine); |
1109 | if (p->activeObjectCreator) { // XXX should only be allowed during begin |
1110 | a->add(a: p->activeObjectCreator->componentAttachment()); |
1111 | } else { |
1112 | QQmlData *d = QQmlData::get(object: obj); |
1113 | Q_ASSERT(d); |
1114 | Q_ASSERT(d->context); |
1115 | a->add(a: &d->context->componentAttached); |
1116 | } |
1117 | |
1118 | return a; |
1119 | } |
1120 | |
1121 | /*! |
1122 | Create an object instance from this component using the provided |
1123 | \a incubator. \a context specifies the context within which to create the object |
1124 | instance. |
1125 | |
1126 | If \a context is 0 (the default), it will create the instance in the |
1127 | engine's \l {QQmlEngine::rootContext()}{root context}. |
1128 | |
1129 | \a forContext specifies a context that this object creation depends upon. |
1130 | If the \a forContext is being created asynchronously, and the |
1131 | \l QQmlIncubator::IncubationMode is \l QQmlIncubator::AsynchronousIfNested, |
1132 | this object will also be created asynchronously. If \a forContext is 0 |
1133 | (the default), the \a context will be used for this decision. |
1134 | |
1135 | The created object and its creation status are available via the |
1136 | \a incubator. |
1137 | |
1138 | \sa QQmlIncubator |
1139 | */ |
1140 | |
1141 | void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context, |
1142 | QQmlContext *forContext) |
1143 | { |
1144 | Q_D(QQmlComponent); |
1145 | |
1146 | if (!context) |
1147 | context = d->engine->rootContext(); |
1148 | |
1149 | QQmlContextData *contextData = QQmlContextData::get(context); |
1150 | QQmlContextData *forContextData = contextData; |
1151 | if (forContext) forContextData = QQmlContextData::get(context: forContext); |
1152 | |
1153 | if (!contextData->isValid()) { |
1154 | qWarning(msg: "QQmlComponent: Cannot create a component in an invalid context" ); |
1155 | return; |
1156 | } |
1157 | |
1158 | if (contextData->engine != d->engine) { |
1159 | qWarning(msg: "QQmlComponent: Must create component in context from the same QQmlEngine" ); |
1160 | return; |
1161 | } |
1162 | |
1163 | if (!isReady()) { |
1164 | qWarning(msg: "QQmlComponent: Component is not ready" ); |
1165 | return; |
1166 | } |
1167 | |
1168 | incubator.clear(); |
1169 | QExplicitlySharedDataPointer<QQmlIncubatorPrivate> p(incubator.d); |
1170 | |
1171 | QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(e: d->engine); |
1172 | |
1173 | p->compilationUnit = d->compilationUnit; |
1174 | p->enginePriv = enginePriv; |
1175 | p->creator.reset(other: new QQmlObjectCreator(contextData, d->compilationUnit, d->creationContext, p.data())); |
1176 | p->subComponentToCreate = d->start; |
1177 | |
1178 | enginePriv->incubate(incubator, forContextData); |
1179 | } |
1180 | |
1181 | /*! |
1182 | Set toplevel \a properties of the \a component. |
1183 | |
1184 | |
1185 | This method provides advanced control over component instance creation. |
1186 | In general, programmers should use |
1187 | \l QQmlComponent::createWithInitialProperties to create a component. |
1188 | |
1189 | Use this method after beginCreate and before completeCreate has been called. |
1190 | If a provided property does not exist, a warning is issued. |
1191 | |
1192 | \since 5.14 |
1193 | */ |
1194 | void QQmlComponent::setInitialProperties(QObject *component, const QVariantMap &properties) |
1195 | { |
1196 | Q_D(QQmlComponent); |
1197 | for (auto it = properties.constBegin(); it != properties.constEnd(); ++it) |
1198 | d->setInitialProperty(component, name: it.key(), value: it.value()); |
1199 | } |
1200 | |
1201 | /* |
1202 | This is essentially a copy of QQmlComponent::create(); except it takes the QQmlContextData |
1203 | arguments instead of QQmlContext which means we don't have to construct the rather weighty |
1204 | wrapper class for every delegate item. |
1205 | |
1206 | This is used by QQmlDelegateModel. |
1207 | */ |
1208 | void QQmlComponentPrivate::incubateObject( |
1209 | QQmlIncubator *incubationTask, |
1210 | QQmlComponent *component, |
1211 | QQmlEngine *engine, |
1212 | QQmlContextData *context, |
1213 | QQmlContextData *forContext) |
1214 | { |
1215 | QQmlIncubatorPrivate *incubatorPriv = QQmlIncubatorPrivate::get(incubator: incubationTask); |
1216 | QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(e: engine); |
1217 | QQmlComponentPrivate *componentPriv = QQmlComponentPrivate::get(c: component); |
1218 | |
1219 | incubatorPriv->compilationUnit = componentPriv->compilationUnit; |
1220 | incubatorPriv->enginePriv = enginePriv; |
1221 | incubatorPriv->creator.reset(other: new QQmlObjectCreator(context, componentPriv->compilationUnit, componentPriv->creationContext)); |
1222 | incubatorPriv->subComponentToCreate = componentPriv->start; |
1223 | |
1224 | enginePriv->incubate(*incubationTask, forContext); |
1225 | } |
1226 | |
1227 | |
1228 | |
1229 | class QQmlComponentIncubator; |
1230 | |
1231 | namespace QV4 { |
1232 | |
1233 | namespace Heap { |
1234 | |
1235 | #define QmlIncubatorObjectMembers(class, Member) \ |
1236 | Member(class, HeapValue, HeapValue, valuemap) \ |
1237 | Member(class, HeapValue, HeapValue, statusChanged) \ |
1238 | Member(class, Pointer, QmlContext *, qmlContext) \ |
1239 | Member(class, NoMark, QQmlComponentIncubator *, incubator) \ |
1240 | Member(class, NoMark, QQmlQPointer<QObject>, parent) |
1241 | |
1242 | DECLARE_HEAP_OBJECT(QmlIncubatorObject, Object) { |
1243 | DECLARE_MARKOBJECTS(QmlIncubatorObject); |
1244 | |
1245 | void init(QQmlIncubator::IncubationMode = QQmlIncubator::Asynchronous); |
1246 | inline void destroy(); |
1247 | }; |
1248 | |
1249 | } |
1250 | |
1251 | struct QmlIncubatorObject : public QV4::Object |
1252 | { |
1253 | V4_OBJECT2(QmlIncubatorObject, Object) |
1254 | V4_NEEDS_DESTROY |
1255 | |
1256 | static ReturnedValue method_get_statusChanged(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); |
1257 | static ReturnedValue method_set_statusChanged(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); |
1258 | static ReturnedValue method_get_status(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); |
1259 | static ReturnedValue method_get_object(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); |
1260 | static ReturnedValue method_forceCompletion(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); |
1261 | |
1262 | void statusChanged(QQmlIncubator::Status); |
1263 | void setInitialState(QObject *, RequiredProperties &requiredProperties); |
1264 | }; |
1265 | |
1266 | } |
1267 | |
1268 | DEFINE_OBJECT_VTABLE(QV4::QmlIncubatorObject); |
1269 | |
1270 | class QQmlComponentIncubator : public QQmlIncubator |
1271 | { |
1272 | public: |
1273 | QQmlComponentIncubator(QV4::Heap::QmlIncubatorObject *inc, IncubationMode mode) |
1274 | : QQmlIncubator(mode) |
1275 | { |
1276 | incubatorObject.set(engine: inc->internalClass->engine, obj: inc); |
1277 | } |
1278 | |
1279 | void statusChanged(Status s) override { |
1280 | QV4::Scope scope(incubatorObject.engine()); |
1281 | QV4::Scoped<QV4::QmlIncubatorObject> i(scope, incubatorObject.as<QV4::QmlIncubatorObject>()); |
1282 | i->statusChanged(s); |
1283 | } |
1284 | |
1285 | void setInitialState(QObject *o) override { |
1286 | QV4::Scope scope(incubatorObject.engine()); |
1287 | QV4::Scoped<QV4::QmlIncubatorObject> i(scope, incubatorObject.as<QV4::QmlIncubatorObject>()); |
1288 | auto d = QQmlIncubatorPrivate::get(incubator: this); |
1289 | i->setInitialState(o, requiredProperties&: d->requiredProperties()); |
1290 | } |
1291 | |
1292 | QV4::PersistentValue incubatorObject; // keep a strong internal reference while incubating |
1293 | }; |
1294 | |
1295 | |
1296 | static void QQmlComponent_setQmlParent(QObject *me, QObject *parent) |
1297 | { |
1298 | if (parent) { |
1299 | me->setParent(parent); |
1300 | typedef QQmlPrivate::AutoParentFunction APF; |
1301 | QList<APF> functions = QQmlMetaType::parentFunctions(); |
1302 | |
1303 | bool needParent = false; |
1304 | for (int ii = 0; ii < functions.count(); ++ii) { |
1305 | QQmlPrivate::AutoParentResult res = functions.at(i: ii)(me, parent); |
1306 | if (res == QQmlPrivate::Parented) { |
1307 | needParent = false; |
1308 | break; |
1309 | } else if (res == QQmlPrivate::IncompatibleParent) { |
1310 | needParent = true; |
1311 | } |
1312 | } |
1313 | if (needParent) |
1314 | qWarning(msg: "QQmlComponent: Created graphical object was not " |
1315 | "placed in the graphics scene." ); |
1316 | } |
1317 | } |
1318 | |
1319 | /*! |
1320 | \qmlmethod object Component::createObject(QtObject parent, object properties) |
1321 | |
1322 | Creates and returns an object instance of this component that will have |
1323 | the given \a parent and \a properties. The \a properties argument is optional. |
1324 | Returns null if object creation fails. |
1325 | |
1326 | The object will be created in the same context as the one in which the component |
1327 | was created. This function will always return null when called on components |
1328 | which were not created in QML. |
1329 | |
1330 | If you wish to create an object without setting a parent, specify \c null for |
1331 | the \a parent value. Note that if the returned object is to be displayed, you |
1332 | must provide a valid \a parent value or set the returned object's \l{Item::parent}{parent} |
1333 | property, otherwise the object will not be visible. |
1334 | |
1335 | If a \a parent is not provided to createObject(), a reference to the returned object must be held so that |
1336 | it is not destroyed by the garbage collector. This is true regardless of whether \l{Item::parent} is set afterwards, |
1337 | because setting the Item parent does not change object ownership. Only the graphical parent is changed. |
1338 | |
1339 | As of \c {QtQuick 1.1}, this method accepts an optional \a properties argument that specifies a |
1340 | map of initial property values for the created object. These values are applied before the object |
1341 | creation is finalized. This is more efficient than setting property values after object creation, |
1342 | particularly where large sets of property values are defined, and also allows property bindings |
1343 | to be set up (using \l{Qt::binding}{Qt.binding}) before the object is created. |
1344 | |
1345 | The \a properties argument is specified as a map of property-value items. For example, the code |
1346 | below creates an object with initial \c x and \c y values of 100 and 100, respectively: |
1347 | |
1348 | \js |
1349 | var component = Qt.createComponent("Button.qml"); |
1350 | if (component.status == Component.Ready) |
1351 | component.createObject(parent, {x: 100, y: 100}); |
1352 | \endjs |
1353 | |
1354 | Dynamically created instances can be deleted with the \c destroy() method. |
1355 | See \l {Dynamic QML Object Creation from JavaScript} for more information. |
1356 | |
1357 | \sa incubateObject() |
1358 | */ |
1359 | |
1360 | |
1361 | void QQmlComponentPrivate::setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v, RequiredProperties &requiredProperties, QObject *createdComponent) |
1362 | { |
1363 | QV4::Scope scope(engine); |
1364 | QV4::ScopedObject object(scope); |
1365 | QV4::ScopedObject valueMap(scope, v); |
1366 | QV4::ObjectIterator it(scope, valueMap, QV4::ObjectIterator::EnumerableOnly); |
1367 | QV4::ScopedString name(scope); |
1368 | QV4::ScopedValue val(scope); |
1369 | if (engine->hasException) |
1370 | return; |
1371 | |
1372 | QV4::ScopedStackFrame frame(scope, qmlContext->d()); |
1373 | |
1374 | while (1) { |
1375 | name = it.nextPropertyNameAsString(value: val); |
1376 | if (!name) |
1377 | break; |
1378 | object = o; |
1379 | const QStringList properties = name->toQString().split(sep: QLatin1Char('.')); |
1380 | bool isTopLevelProperty = properties.size() == 1; |
1381 | for (int i = 0; i < properties.length() - 1; ++i) { |
1382 | name = engine->newString(s: properties.at(i)); |
1383 | object = object->get(name); |
1384 | if (engine->hasException || !object) { |
1385 | break; |
1386 | } |
1387 | } |
1388 | if (engine->hasException || !object) { |
1389 | engine->hasException = false; |
1390 | continue; |
1391 | } |
1392 | name = engine->newString(s: properties.last()); |
1393 | object->put(name, v: val); |
1394 | if (engine->hasException) { |
1395 | engine->hasException = false; |
1396 | continue; |
1397 | } else if (isTopLevelProperty) { |
1398 | auto prop = removePropertyFromRequired(createdComponent, name: name->toQString(), requiredProperties); |
1399 | } |
1400 | } |
1401 | |
1402 | engine->hasException = false; |
1403 | } |
1404 | |
1405 | QQmlError QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(const RequiredPropertyInfo &unsetRequiredProperty) |
1406 | { |
1407 | QQmlError error; |
1408 | QString description = QLatin1String("Required property %1 was not initialized" ).arg(args: unsetRequiredProperty.propertyName); |
1409 | switch (unsetRequiredProperty.aliasesToRequired.size()) { |
1410 | case 0: |
1411 | break; |
1412 | case 1: { |
1413 | const auto info = unsetRequiredProperty.aliasesToRequired.first(); |
1414 | description += QLatin1String("\nIt can be set via the alias property %1 from %2\n" ).arg(args: info.propertyName, args: info.fileUrl.toString()); |
1415 | break; |
1416 | } |
1417 | default: |
1418 | description += QLatin1String("\nIt can be set via one of the following alias properties:" ); |
1419 | for (auto aliasInfo: unsetRequiredProperty.aliasesToRequired) { |
1420 | description += QLatin1String("\n- %1 (%2)" ).arg(args&: aliasInfo.propertyName, args: aliasInfo.fileUrl.toString()); |
1421 | } |
1422 | description += QLatin1Char('\n'); |
1423 | } |
1424 | error.setDescription(description); |
1425 | error.setUrl(unsetRequiredProperty.fileUrl); |
1426 | error.setLine(qmlConvertSourceCoordinate<quint32, int>(n: unsetRequiredProperty.location.line)); |
1427 | error.setColumn(qmlConvertSourceCoordinate<quint32, int>(n: unsetRequiredProperty.location.column)); |
1428 | return error; |
1429 | } |
1430 | |
1431 | /*! |
1432 | \internal |
1433 | */ |
1434 | void QQmlComponent::createObject(QQmlV4Function *args) |
1435 | { |
1436 | Q_D(QQmlComponent); |
1437 | Q_ASSERT(d->engine); |
1438 | Q_ASSERT(args); |
1439 | |
1440 | QObject *parent = nullptr; |
1441 | QV4::ExecutionEngine *v4 = args->v4engine(); |
1442 | QV4::Scope scope(v4); |
1443 | QV4::ScopedValue valuemap(scope, QV4::Value::undefinedValue()); |
1444 | |
1445 | if (args->length() >= 1) { |
1446 | QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, (*args)[0]); |
1447 | if (qobjectWrapper) |
1448 | parent = qobjectWrapper->object(); |
1449 | } |
1450 | |
1451 | if (args->length() >= 2) { |
1452 | QV4::ScopedValue v(scope, (*args)[1]); |
1453 | if (!v->as<QV4::Object>() || v->as<QV4::ArrayObject>()) { |
1454 | qmlWarning(me: this) << tr(s: "createObject: value is not an object" ); |
1455 | args->setReturnValue(QV4::Encode::null()); |
1456 | return; |
1457 | } |
1458 | valuemap = v; |
1459 | } |
1460 | |
1461 | QQmlContext *ctxt = creationContext(); |
1462 | if (!ctxt) ctxt = d->engine->rootContext(); |
1463 | |
1464 | QObject *rv = beginCreate(publicContext: ctxt); |
1465 | |
1466 | if (!rv) { |
1467 | args->setReturnValue(QV4::Encode::null()); |
1468 | return; |
1469 | } |
1470 | |
1471 | QQmlComponent_setQmlParent(me: rv, parent); |
1472 | |
1473 | QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(engine: v4, object: rv)); |
1474 | Q_ASSERT(object->isObject()); |
1475 | |
1476 | if (!valuemap->isUndefined()) { |
1477 | QV4::Scoped<QV4::QmlContext> qmlContext(scope, v4->qmlContext()); |
1478 | QQmlComponentPrivate::setInitialProperties(engine: v4, qmlContext, o: object, v: valuemap, requiredProperties&: d->requiredProperties(), createdComponent: rv); |
1479 | } |
1480 | if (!d->requiredProperties().empty()) { |
1481 | QList<QQmlError> errors; |
1482 | for (const auto &requiredProperty: d->requiredProperties()) { |
1483 | errors.push_back(t: QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(unsetRequiredProperty: requiredProperty)); |
1484 | } |
1485 | qmlWarning(me: rv, errors); |
1486 | args->setReturnValue(QV4::Encode::null()); |
1487 | delete rv; |
1488 | return; |
1489 | } |
1490 | |
1491 | d->completeCreate(); |
1492 | |
1493 | Q_ASSERT(QQmlData::get(rv)); |
1494 | QQmlData::get(object: rv)->explicitIndestructibleSet = false; |
1495 | QQmlData::get(object: rv)->indestructible = false; |
1496 | |
1497 | args->setReturnValue(object->asReturnedValue()); |
1498 | } |
1499 | |
1500 | /*! |
1501 | \qmlmethod object Component::incubateObject(Item parent, object properties, enumeration mode) |
1502 | |
1503 | Creates an incubator for an instance of this component. Incubators allow new component |
1504 | instances to be instantiated asynchronously and do not cause freezes in the UI. |
1505 | |
1506 | The \a parent argument specifies the parent the created instance will have. Omitting the |
1507 | parameter or passing null will create an object with no parent. In this case, a reference |
1508 | to the created object must be held so that it is not destroyed by the garbage collector. |
1509 | |
1510 | The \a properties argument is specified as a map of property-value items which will be |
1511 | set on the created object during its construction. \a mode may be Qt.Synchronous or |
1512 | Qt.Asynchronous, and controls whether the instance is created synchronously or asynchronously. |
1513 | The default is asynchronous. In some circumstances, even if Qt.Synchronous is specified, |
1514 | the incubator may create the object asynchronously. This happens if the component calling |
1515 | incubateObject() is itself being created asynchronously. |
1516 | |
1517 | All three arguments are optional. |
1518 | |
1519 | If successful, the method returns an incubator, otherwise null. The incubator has the following |
1520 | properties: |
1521 | |
1522 | \list |
1523 | \li status The status of the incubator. Valid values are Component.Ready, Component.Loading and |
1524 | Component.Error. |
1525 | \li object The created object instance. Will only be available once the incubator is in the |
1526 | Ready status. |
1527 | \li onStatusChanged Specifies a callback function to be invoked when the status changes. The |
1528 | status is passed as a parameter to the callback. |
1529 | \li forceCompletion() Call to complete incubation synchronously. |
1530 | \endlist |
1531 | |
1532 | The following example demonstrates how to use an incubator: |
1533 | |
1534 | \js |
1535 | var component = Qt.createComponent("Button.qml"); |
1536 | |
1537 | var incubator = component.incubateObject(parent, { x: 10, y: 10 }); |
1538 | if (incubator.status != Component.Ready) { |
1539 | incubator.onStatusChanged = function(status) { |
1540 | if (status == Component.Ready) { |
1541 | print ("Object", incubator.object, "is now ready!"); |
1542 | } |
1543 | } |
1544 | } else { |
1545 | print ("Object", incubator.object, "is ready immediately!"); |
1546 | } |
1547 | \endjs |
1548 | |
1549 | Dynamically created instances can be deleted with the \c destroy() method. |
1550 | See \l {Dynamic QML Object Creation from JavaScript} for more information. |
1551 | |
1552 | \sa createObject() |
1553 | */ |
1554 | |
1555 | /*! |
1556 | \internal |
1557 | */ |
1558 | void QQmlComponent::incubateObject(QQmlV4Function *args) |
1559 | { |
1560 | Q_D(QQmlComponent); |
1561 | Q_ASSERT(d->engine); |
1562 | Q_UNUSED(d); |
1563 | Q_ASSERT(args); |
1564 | QV4::ExecutionEngine *v4 = args->v4engine(); |
1565 | QV4::Scope scope(v4); |
1566 | |
1567 | QObject *parent = nullptr; |
1568 | QV4::ScopedValue valuemap(scope, QV4::Value::undefinedValue()); |
1569 | QQmlIncubator::IncubationMode mode = QQmlIncubator::Asynchronous; |
1570 | |
1571 | if (args->length() >= 1) { |
1572 | QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, (*args)[0]); |
1573 | if (qobjectWrapper) |
1574 | parent = qobjectWrapper->object(); |
1575 | } |
1576 | |
1577 | if (args->length() >= 2) { |
1578 | QV4::ScopedValue v(scope, (*args)[1]); |
1579 | if (v->isNull()) { |
1580 | } else if (!v->as<QV4::Object>() || v->as<QV4::ArrayObject>()) { |
1581 | qmlWarning(me: this) << tr(s: "createObject: value is not an object" ); |
1582 | args->setReturnValue(QV4::Encode::null()); |
1583 | return; |
1584 | } else { |
1585 | valuemap = v; |
1586 | } |
1587 | } |
1588 | |
1589 | if (args->length() >= 3) { |
1590 | QV4::ScopedValue val(scope, (*args)[2]); |
1591 | quint32 v = val->toUInt32(); |
1592 | if (v == 0) |
1593 | mode = QQmlIncubator::Asynchronous; |
1594 | else if (v == 1) |
1595 | mode = QQmlIncubator::AsynchronousIfNested; |
1596 | } |
1597 | |
1598 | QQmlComponentExtension *e = componentExtension(engine: args->v4engine()); |
1599 | |
1600 | QV4::Scoped<QV4::QmlIncubatorObject> r(scope, v4->memoryManager->allocate<QV4::QmlIncubatorObject>(args: mode)); |
1601 | QV4::ScopedObject p(scope, e->incubationProto.value()); |
1602 | r->setPrototypeOf(p); |
1603 | |
1604 | if (!valuemap->isUndefined()) |
1605 | r->d()->valuemap.set(e: scope.engine, newVal: valuemap); |
1606 | r->d()->qmlContext.set(e: scope.engine, newVal: v4->qmlContext()); |
1607 | r->d()->parent = parent; |
1608 | |
1609 | QQmlIncubator *incubator = r->d()->incubator; |
1610 | create(incubator&: *incubator, context: creationContext()); |
1611 | |
1612 | if (incubator->status() == QQmlIncubator::Null) { |
1613 | args->setReturnValue(QV4::Encode::null()); |
1614 | } else { |
1615 | args->setReturnValue(r.asReturnedValue()); |
1616 | } |
1617 | } |
1618 | |
1619 | // XXX used by QSGLoader |
1620 | void QQmlComponentPrivate::initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate, RequiredProperties &requiredProperties) |
1621 | { |
1622 | QV4::ExecutionEngine *v4engine = engine->handle(); |
1623 | QV4::Scope scope(v4engine); |
1624 | |
1625 | QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(engine: v4engine, object: toCreate)); |
1626 | Q_ASSERT(object->as<QV4::Object>()); |
1627 | |
1628 | if (!valuemap.isUndefined()) |
1629 | setInitialProperties(engine: v4engine, qmlContext, o: object, v: valuemap, requiredProperties, createdComponent: toCreate); |
1630 | } |
1631 | |
1632 | QQmlComponentExtension::QQmlComponentExtension(QV4::ExecutionEngine *v4) |
1633 | { |
1634 | QV4::Scope scope(v4); |
1635 | QV4::ScopedObject proto(scope, v4->newObject()); |
1636 | proto->defineAccessorProperty(QStringLiteral("onStatusChanged" ), |
1637 | getter: QV4::QmlIncubatorObject::method_get_statusChanged, setter: QV4::QmlIncubatorObject::method_set_statusChanged); |
1638 | proto->defineAccessorProperty(QStringLiteral("status" ), getter: QV4::QmlIncubatorObject::method_get_status, setter: nullptr); |
1639 | proto->defineAccessorProperty(QStringLiteral("object" ), getter: QV4::QmlIncubatorObject::method_get_object, setter: nullptr); |
1640 | proto->defineDefaultProperty(QStringLiteral("forceCompletion" ), code: QV4::QmlIncubatorObject::method_forceCompletion); |
1641 | |
1642 | incubationProto.set(engine: v4, value: proto); |
1643 | } |
1644 | |
1645 | QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_object(const FunctionObject *b, const Value *thisObject, const Value *, int) |
1646 | { |
1647 | QV4::Scope scope(b); |
1648 | QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>()); |
1649 | if (!o) |
1650 | THROW_TYPE_ERROR(); |
1651 | |
1652 | return QV4::QObjectWrapper::wrap(engine: scope.engine, object: o->d()->incubator->object()); |
1653 | } |
1654 | |
1655 | QV4::ReturnedValue QV4::QmlIncubatorObject::method_forceCompletion(const FunctionObject *b, const Value *thisObject, const Value *, int) |
1656 | { |
1657 | QV4::Scope scope(b); |
1658 | QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>()); |
1659 | if (!o) |
1660 | THROW_TYPE_ERROR(); |
1661 | |
1662 | o->d()->incubator->forceCompletion(); |
1663 | |
1664 | RETURN_UNDEFINED(); |
1665 | } |
1666 | |
1667 | QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_status(const FunctionObject *b, const Value *thisObject, const Value *, int) |
1668 | { |
1669 | QV4::Scope scope(b); |
1670 | QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>()); |
1671 | if (!o) |
1672 | THROW_TYPE_ERROR(); |
1673 | |
1674 | return QV4::Encode(o->d()->incubator->status()); |
1675 | } |
1676 | |
1677 | QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_statusChanged(const FunctionObject *b, const Value *thisObject, const Value *, int) |
1678 | { |
1679 | QV4::Scope scope(b); |
1680 | QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>()); |
1681 | if (!o) |
1682 | THROW_TYPE_ERROR(); |
1683 | |
1684 | return QV4::Encode(o->d()->statusChanged); |
1685 | } |
1686 | |
1687 | QV4::ReturnedValue QV4::QmlIncubatorObject::method_set_statusChanged(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
1688 | { |
1689 | QV4::Scope scope(b); |
1690 | QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>()); |
1691 | if (!o || argc < 1) |
1692 | THROW_TYPE_ERROR(); |
1693 | |
1694 | o->d()->statusChanged.set(e: scope.engine, newVal: argv[0]); |
1695 | |
1696 | RETURN_UNDEFINED(); |
1697 | } |
1698 | |
1699 | QQmlComponentExtension::~QQmlComponentExtension() |
1700 | { |
1701 | } |
1702 | |
1703 | void QV4::Heap::QmlIncubatorObject::init(QQmlIncubator::IncubationMode m) |
1704 | { |
1705 | Object::init(); |
1706 | valuemap.set(e: internalClass->engine, newVal: QV4::Value::undefinedValue()); |
1707 | statusChanged.set(e: internalClass->engine, newVal: QV4::Value::undefinedValue()); |
1708 | parent.init(); |
1709 | qmlContext.set(e: internalClass->engine, newVal: nullptr); |
1710 | incubator = new QQmlComponentIncubator(this, m); |
1711 | } |
1712 | |
1713 | void QV4::Heap::QmlIncubatorObject::destroy() { |
1714 | delete incubator; |
1715 | parent.destroy(); |
1716 | Object::destroy(); |
1717 | } |
1718 | |
1719 | void QV4::QmlIncubatorObject::setInitialState(QObject *o, RequiredProperties &requiredProperties) |
1720 | { |
1721 | QQmlComponent_setQmlParent(me: o, parent: d()->parent); |
1722 | |
1723 | if (!d()->valuemap.isUndefined()) { |
1724 | QV4::ExecutionEngine *v4 = engine(); |
1725 | QV4::Scope scope(v4); |
1726 | QV4::ScopedObject obj(scope, QV4::QObjectWrapper::wrap(engine: v4, object: o)); |
1727 | QV4::Scoped<QV4::QmlContext> qmlCtxt(scope, d()->qmlContext); |
1728 | QQmlComponentPrivate::setInitialProperties(engine: v4, qmlContext: qmlCtxt, o: obj, v: d()->valuemap, requiredProperties, createdComponent: o); |
1729 | } |
1730 | } |
1731 | |
1732 | void QV4::QmlIncubatorObject::statusChanged(QQmlIncubator::Status s) |
1733 | { |
1734 | QV4::Scope scope(engine()); |
1735 | // hold the incubated object in a scoped value to prevent it's destruction before this method returns |
1736 | QV4::ScopedObject incubatedObject(scope, QV4::QObjectWrapper::wrap(engine: scope.engine, object: d()->incubator->object())); |
1737 | |
1738 | if (s == QQmlIncubator::Ready) { |
1739 | Q_ASSERT(QQmlData::get(d()->incubator->object())); |
1740 | QQmlData::get(object: d()->incubator->object())->explicitIndestructibleSet = false; |
1741 | QQmlData::get(object: d()->incubator->object())->indestructible = false; |
1742 | } |
1743 | |
1744 | QV4::ScopedFunctionObject f(scope, d()->statusChanged); |
1745 | if (f) { |
1746 | QV4::JSCallData jsCallData(scope, 1); |
1747 | *jsCallData->thisObject = this; |
1748 | jsCallData->args[0] = QV4::Value::fromUInt32(i: s); |
1749 | f->call(data: jsCallData); |
1750 | if (scope.hasException()) { |
1751 | QQmlError error = scope.engine->catchExceptionAsQmlError(); |
1752 | QQmlEnginePrivate::warning(QQmlEnginePrivate::get(e: scope.engine->qmlEngine()), error); |
1753 | } |
1754 | } |
1755 | |
1756 | if (s != QQmlIncubator::Loading) |
1757 | d()->incubator->incubatorObject.clear(); |
1758 | } |
1759 | |
1760 | #undef INITIALPROPERTIES_SOURCE |
1761 | |
1762 | QT_END_NAMESPACE |
1763 | |
1764 | #include "moc_qqmlcomponent.cpp" |
1765 | |