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 | const bool isValid = prop.isValid(); |
379 | if (!isValid || !privProp->writeValueProperty(value, {})) { |
380 | QQmlError error{}; |
381 | error.setUrl(url); |
382 | if (isValid) |
383 | error.setDescription(QLatin1String("Could not set initial property %1" ).arg(args: name)); |
384 | else |
385 | error.setDescription(QLatin1String("Setting initial properties failed: %2 does not have a property called %1" ).arg(args: name, |
386 | args: QQmlMetaType::prettyTypeName(object: component))); |
387 | state.errors.push_back(t: error); |
388 | return false; |
389 | } else |
390 | return true; |
391 | } |
392 | |
393 | /*! |
394 | \internal |
395 | */ |
396 | QQmlComponent::QQmlComponent(QObject *parent) |
397 | : QObject(*(new QQmlComponentPrivate), parent) |
398 | { |
399 | } |
400 | |
401 | /*! |
402 | Destruct the QQmlComponent. |
403 | */ |
404 | QQmlComponent::~QQmlComponent() |
405 | { |
406 | Q_D(QQmlComponent); |
407 | |
408 | if (d->state.completePending) { |
409 | qWarning(msg: "QQmlComponent: Component destroyed while completion pending" ); |
410 | |
411 | if (isError()) { |
412 | qWarning() << "This may have been caused by one of the following errors:" ; |
413 | for (const QQmlError &error : qAsConst(t&: d->state.errors)) |
414 | qWarning().nospace().noquote() << QLatin1String(" " ) << error; |
415 | } |
416 | |
417 | d->completeCreate(); |
418 | } |
419 | |
420 | if (d->typeData) { |
421 | d->typeData->unregisterCallback(d); |
422 | d->typeData = nullptr; |
423 | } |
424 | } |
425 | |
426 | /*! |
427 | \qmlproperty enumeration Component::status |
428 | This property holds the status of component loading. The status can be one of the |
429 | following: |
430 | \list |
431 | \li Component.Null - no data is available for the component |
432 | \li Component.Ready - the component has been loaded, and can be used to create instances. |
433 | \li Component.Loading - the component is currently being loaded |
434 | \li Component.Error - an error occurred while loading the component. |
435 | Calling errorString() will provide a human-readable description of any errors. |
436 | \endlist |
437 | */ |
438 | |
439 | /*! |
440 | \property QQmlComponent::status |
441 | The component's current \l{QQmlComponent::Status} {status}. |
442 | */ |
443 | QQmlComponent::Status QQmlComponent::status() const |
444 | { |
445 | Q_D(const QQmlComponent); |
446 | |
447 | if (d->typeData) |
448 | return Loading; |
449 | else if (!d->state.errors.isEmpty()) |
450 | return Error; |
451 | else if (d->engine && d->compilationUnit) |
452 | return Ready; |
453 | else |
454 | return Null; |
455 | } |
456 | |
457 | /*! |
458 | Returns true if status() == QQmlComponent::Null. |
459 | */ |
460 | bool QQmlComponent::isNull() const |
461 | { |
462 | return status() == Null; |
463 | } |
464 | |
465 | /*! |
466 | Returns true if status() == QQmlComponent::Ready. |
467 | */ |
468 | bool QQmlComponent::isReady() const |
469 | { |
470 | return status() == Ready; |
471 | } |
472 | |
473 | /*! |
474 | Returns true if status() == QQmlComponent::Error. |
475 | */ |
476 | bool QQmlComponent::isError() const |
477 | { |
478 | return status() == Error; |
479 | } |
480 | |
481 | /*! |
482 | Returns true if status() == QQmlComponent::Loading. |
483 | */ |
484 | bool QQmlComponent::isLoading() const |
485 | { |
486 | return status() == Loading; |
487 | } |
488 | |
489 | /*! |
490 | \qmlproperty real Component::progress |
491 | The progress of loading the component, from 0.0 (nothing loaded) |
492 | to 1.0 (finished). |
493 | */ |
494 | |
495 | /*! |
496 | \property QQmlComponent::progress |
497 | The progress of loading the component, from 0.0 (nothing loaded) |
498 | to 1.0 (finished). |
499 | */ |
500 | qreal QQmlComponent::progress() const |
501 | { |
502 | Q_D(const QQmlComponent); |
503 | return d->progress; |
504 | } |
505 | |
506 | /*! |
507 | \fn void QQmlComponent::progressChanged(qreal progress) |
508 | |
509 | Emitted whenever the component's loading progress changes. \a progress will be the |
510 | current progress between 0.0 (nothing loaded) and 1.0 (finished). |
511 | */ |
512 | |
513 | /*! |
514 | \fn void QQmlComponent::statusChanged(QQmlComponent::Status status) |
515 | |
516 | Emitted whenever the component's status changes. \a status will be the |
517 | new status. |
518 | */ |
519 | |
520 | /*! |
521 | Create a QQmlComponent with no data and give it the specified |
522 | \a engine and \a parent. Set the data with setData(). |
523 | */ |
524 | QQmlComponent::QQmlComponent(QQmlEngine *engine, QObject *parent) |
525 | : QObject(*(new QQmlComponentPrivate), parent) |
526 | { |
527 | Q_D(QQmlComponent); |
528 | d->engine = engine; |
529 | } |
530 | |
531 | /*! |
532 | Create a QQmlComponent from the given \a url and give it the |
533 | specified \a parent and \a engine. |
534 | |
535 | \include qqmlcomponent.qdoc url-note |
536 | |
537 | \sa loadUrl() |
538 | */ |
539 | QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, QObject *parent) |
540 | : QQmlComponent(engine, url, QQmlComponent::PreferSynchronous, parent) |
541 | { |
542 | } |
543 | |
544 | /*! |
545 | Create a QQmlComponent from the given \a url and give it the |
546 | specified \a parent and \a engine. If \a mode is \l Asynchronous, |
547 | the component will be loaded and compiled asynchronously. |
548 | |
549 | \include qqmlcomponent.qdoc url-note |
550 | |
551 | \sa loadUrl() |
552 | */ |
553 | QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, CompilationMode mode, |
554 | QObject *parent) |
555 | : QQmlComponent(engine, parent) |
556 | { |
557 | Q_D(QQmlComponent); |
558 | d->loadUrl(newUrl: url, mode); |
559 | } |
560 | |
561 | /*! |
562 | Create a QQmlComponent from the given \a fileName and give it the specified |
563 | \a parent and \a engine. |
564 | |
565 | \sa loadUrl() |
566 | */ |
567 | QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName, |
568 | QObject *parent) |
569 | : QQmlComponent(engine, fileName, QQmlComponent::PreferSynchronous, parent) |
570 | { |
571 | } |
572 | |
573 | /*! |
574 | Create a QQmlComponent from the given \a fileName and give it the specified |
575 | \a parent and \a engine. If \a mode is \l Asynchronous, |
576 | the component will be loaded and compiled asynchronously. |
577 | |
578 | \sa loadUrl() |
579 | */ |
580 | QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName, |
581 | CompilationMode mode, QObject *parent) |
582 | : QQmlComponent(engine, parent) |
583 | { |
584 | Q_D(QQmlComponent); |
585 | const QUrl url = QDir::isAbsolutePath(path: fileName) ? QUrl::fromLocalFile(localfile: fileName) : QUrl(fileName); |
586 | d->loadUrl(newUrl: url, mode); |
587 | } |
588 | |
589 | /*! |
590 | \internal |
591 | */ |
592 | QQmlComponent::QQmlComponent(QQmlEngine *engine, QV4::ExecutableCompilationUnit *compilationUnit, |
593 | int start, QObject *parent) |
594 | : QQmlComponent(engine, parent) |
595 | { |
596 | Q_D(QQmlComponent); |
597 | d->compilationUnit = compilationUnit; |
598 | d->start = start; |
599 | d->url = compilationUnit->finalUrl(); |
600 | d->progress = 1.0; |
601 | } |
602 | |
603 | /*! |
604 | Sets the QQmlComponent to use the given QML \a data. If \a url |
605 | is provided, it is used to set the component name and to provide |
606 | a base path for items resolved by this component. |
607 | */ |
608 | void QQmlComponent::setData(const QByteArray &data, const QUrl &url) |
609 | { |
610 | Q_D(QQmlComponent); |
611 | |
612 | if (!d->engine) { |
613 | // ###Qt6: In Qt 6, it should be impossible for users to create a QQmlComponent without an engine, and we can remove this check |
614 | qWarning(msg: "QQmlComponent: Must provide an engine before calling setData" ); |
615 | return; |
616 | } |
617 | |
618 | d->clear(); |
619 | |
620 | d->url = url; |
621 | |
622 | QQmlRefPointer<QQmlTypeData> typeData = QQmlEnginePrivate::get(e: d->engine)->typeLoader.getType(data, url); |
623 | |
624 | if (typeData->isCompleteOrError()) { |
625 | d->fromTypeData(data: typeData); |
626 | } else { |
627 | d->typeData = typeData; |
628 | d->typeData->registerCallback(d); |
629 | } |
630 | |
631 | d->progress = 1.0; |
632 | emit statusChanged(status()); |
633 | emit progressChanged(d->progress); |
634 | } |
635 | |
636 | /*! |
637 | Returns the QQmlContext the component was created in. This is only |
638 | valid for components created directly from QML. |
639 | */ |
640 | QQmlContext *QQmlComponent::creationContext() const |
641 | { |
642 | Q_D(const QQmlComponent); |
643 | if(d->creationContext) |
644 | return d->creationContext->asQQmlContext(); |
645 | |
646 | return qmlContext(this); |
647 | } |
648 | |
649 | /*! |
650 | Returns the QQmlEngine of this component. |
651 | |
652 | \since 5.12 |
653 | */ |
654 | QQmlEngine *QQmlComponent::engine() const |
655 | { |
656 | Q_D(const QQmlComponent); |
657 | return d->engine; |
658 | } |
659 | |
660 | /*! |
661 | Load the QQmlComponent from the provided \a url. |
662 | |
663 | \include qqmlcomponent.qdoc url-note |
664 | */ |
665 | void QQmlComponent::loadUrl(const QUrl &url) |
666 | { |
667 | Q_D(QQmlComponent); |
668 | d->loadUrl(newUrl: url); |
669 | } |
670 | |
671 | /*! |
672 | Load the QQmlComponent from the provided \a url. |
673 | If \a mode is \l Asynchronous, the component will be loaded and compiled asynchronously. |
674 | |
675 | \include qqmlcomponent.qdoc url-note |
676 | */ |
677 | void QQmlComponent::loadUrl(const QUrl &url, QQmlComponent::CompilationMode mode) |
678 | { |
679 | Q_D(QQmlComponent); |
680 | d->loadUrl(newUrl: url, mode); |
681 | } |
682 | |
683 | void QQmlComponentPrivate::loadUrl(const QUrl &newUrl, QQmlComponent::CompilationMode mode) |
684 | { |
685 | Q_Q(QQmlComponent); |
686 | clear(); |
687 | |
688 | if (newUrl.isRelative()) { |
689 | // The new URL is a relative URL like QUrl("main.qml"). |
690 | url = engine->baseUrl().resolved(relative: QUrl(newUrl.toString())); |
691 | } else if (engine->baseUrl().isLocalFile() && newUrl.isLocalFile() && !QDir::isAbsolutePath(path: newUrl.toLocalFile())) { |
692 | // The new URL is a file on disk but it's a relative path; e.g.: |
693 | // QUrl::fromLocalFile("main.qml") or QUrl("file:main.qml") |
694 | // We need to remove the scheme so that it becomes a relative URL with a relative path: |
695 | QUrl fixedUrl(newUrl); |
696 | fixedUrl.setScheme(QString()); |
697 | // Then, turn it into an absolute URL with an absolute path by resolving it against the engine's baseUrl(). |
698 | // This is a compatibility hack for QTBUG-58837. |
699 | url = engine->baseUrl().resolved(relative: fixedUrl); |
700 | } else { |
701 | url = newUrl; |
702 | } |
703 | |
704 | if (newUrl.isEmpty()) { |
705 | QQmlError error; |
706 | error.setDescription(QQmlComponent::tr(s: "Invalid empty URL" )); |
707 | state.errors << error; |
708 | return; |
709 | } |
710 | |
711 | if (progress != 0.0) { |
712 | progress = 0.0; |
713 | emit q->progressChanged(progress); |
714 | } |
715 | |
716 | QQmlTypeLoader::Mode loaderMode = (mode == QQmlComponent::Asynchronous) |
717 | ? QQmlTypeLoader::Asynchronous |
718 | : QQmlTypeLoader::PreferSynchronous; |
719 | |
720 | QQmlRefPointer<QQmlTypeData> data = QQmlEnginePrivate::get(e: engine)->typeLoader.getType(unNormalizedUrl: url, mode: loaderMode); |
721 | |
722 | if (data->isCompleteOrError()) { |
723 | fromTypeData(data); |
724 | progress = 1.0; |
725 | } else { |
726 | typeData = data; |
727 | typeData->registerCallback(this); |
728 | progress = data->progress(); |
729 | } |
730 | |
731 | emit q->statusChanged(q->status()); |
732 | if (progress != 0.0) |
733 | emit q->progressChanged(progress); |
734 | } |
735 | |
736 | /*! |
737 | Returns the list of errors that occurred during the last compile or create |
738 | operation. An empty list is returned if isError() is not set. |
739 | */ |
740 | QList<QQmlError> QQmlComponent::errors() const |
741 | { |
742 | Q_D(const QQmlComponent); |
743 | if (isError()) |
744 | return d->state.errors; |
745 | else |
746 | return QList<QQmlError>(); |
747 | } |
748 | |
749 | /*! |
750 | \qmlmethod string Component::errorString() |
751 | |
752 | Returns a human-readable description of any error. |
753 | |
754 | The string includes the file, location, and description of each error. |
755 | If multiple errors are present, they are separated by a newline character. |
756 | |
757 | If no errors are present, an empty string is returned. |
758 | */ |
759 | |
760 | /*! |
761 | \internal |
762 | errorString is only meant as a way to get the errors in script |
763 | */ |
764 | QString QQmlComponent::errorString() const |
765 | { |
766 | Q_D(const QQmlComponent); |
767 | QString ret; |
768 | if(!isError()) |
769 | return ret; |
770 | for (const QQmlError &e : d->state.errors) { |
771 | ret += e.url().toString() + QLatin1Char(':') + |
772 | QString::number(e.line()) + QLatin1Char(' ') + |
773 | e.description() + QLatin1Char('\n'); |
774 | } |
775 | return ret; |
776 | } |
777 | |
778 | /*! |
779 | \qmlproperty url Component::url |
780 | The component URL. This is the URL that was used to construct the component. |
781 | */ |
782 | |
783 | /*! |
784 | \property QQmlComponent::url |
785 | The component URL. This is the URL passed to either the constructor, |
786 | or the loadUrl(), or setData() methods. |
787 | */ |
788 | QUrl QQmlComponent::url() const |
789 | { |
790 | Q_D(const QQmlComponent); |
791 | return d->url; |
792 | } |
793 | |
794 | /*! |
795 | \internal |
796 | */ |
797 | QQmlComponent::QQmlComponent(QQmlComponentPrivate &dd, QObject *parent) |
798 | : QObject(dd, parent) |
799 | { |
800 | } |
801 | |
802 | /*! |
803 | Create an object instance from this component. Returns \nullptr if creation |
804 | failed. \a context specifies the context within which to create the object |
805 | instance. |
806 | |
807 | If \a context is \nullptr (the default), it will create the instance in the |
808 | \l {QQmlEngine::rootContext()}{root context} of the engine. |
809 | |
810 | The ownership of the returned object instance is transferred to the caller. |
811 | |
812 | If the object being created from this component is a visual item, it must |
813 | have a visual parent, which can be set by calling |
814 | QQuickItem::setParentItem(). See \l {Concepts - Visual Parent in Qt Quick} |
815 | for more details. |
816 | |
817 | \sa QQmlEngine::ObjectOwnership |
818 | */ |
819 | QObject *QQmlComponent::create(QQmlContext *context) |
820 | { |
821 | Q_D(QQmlComponent); |
822 | |
823 | QObject *rv = d->doBeginCreate(q: this, context); |
824 | if (rv) |
825 | completeCreate(); |
826 | if (rv && !d->requiredProperties().empty()) { |
827 | delete rv; |
828 | return nullptr; |
829 | } |
830 | return rv; |
831 | } |
832 | |
833 | /*! |
834 | Create an object instance of this component, and initialize its toplevel |
835 | properties with \a initialProperties. \a context specifies the context |
836 | where the object instance is to be created. |
837 | |
838 | \omit |
839 | TODO: also mention errorString() when QTBUG-93239 is fixed |
840 | \endomit |
841 | |
842 | If any of the \c initialProperties cannot be set, \l isError() will return |
843 | \c true, and the \l errors() function can be used to |
844 | get detailed information about the error(s). |
845 | |
846 | \sa QQmlComponent::create |
847 | \since 5.14 |
848 | */ |
849 | QObject *QQmlComponent::createWithInitialProperties(const QVariantMap& initialProperties, QQmlContext *context) |
850 | { |
851 | Q_D(QQmlComponent); |
852 | |
853 | QObject *rv = d->doBeginCreate(q: this, context); |
854 | if (rv) { |
855 | setInitialProperties(component: rv, properties: initialProperties); |
856 | completeCreate(); |
857 | } |
858 | if (!d->requiredProperties().empty()) { |
859 | d->requiredProperties().clear(); |
860 | return nullptr; |
861 | } |
862 | return rv; |
863 | } |
864 | |
865 | /*! |
866 | This method provides advanced control over component instance creation. |
867 | In general, programmers should use QQmlComponent::create() to create object |
868 | instances. |
869 | |
870 | Create an object instance from this component. Returns \nullptr if creation |
871 | failed. \a publicContext specifies the context within which to create the object |
872 | instance. |
873 | |
874 | When QQmlComponent constructs an instance, it occurs in three steps: |
875 | \list 1 |
876 | \li The object hierarchy is created, and constant values are assigned. |
877 | \li Property bindings are evaluated for the first time. |
878 | \li If applicable, QQmlParserStatus::componentComplete() is called on objects. |
879 | \endlist |
880 | QQmlComponent::beginCreate() differs from QQmlComponent::create() in that it |
881 | only performs step 1. QQmlComponent::completeCreate() must be called to |
882 | complete steps 2 and 3. |
883 | |
884 | This breaking point is sometimes useful when using attached properties to |
885 | communicate information to an instantiated component, as it allows their |
886 | initial values to be configured before property bindings take effect. |
887 | |
888 | The ownership of the returned object instance is transferred to the caller. |
889 | |
890 | \sa completeCreate(), QQmlEngine::ObjectOwnership |
891 | */ |
892 | QObject *QQmlComponent::beginCreate(QQmlContext *publicContext) |
893 | { |
894 | Q_D(QQmlComponent); |
895 | |
896 | Q_ASSERT(publicContext); |
897 | QQmlContextData *context = QQmlContextData::get(context: publicContext); |
898 | |
899 | return d->beginCreate(context); |
900 | } |
901 | |
902 | QObject * |
903 | QQmlComponentPrivate::beginCreate(QQmlContextData *context) |
904 | { |
905 | Q_Q(QQmlComponent); |
906 | if (!context) { |
907 | qWarning(msg: "QQmlComponent: Cannot create a component in a null context" ); |
908 | return nullptr; |
909 | } |
910 | |
911 | if (!context->isValid()) { |
912 | qWarning(msg: "QQmlComponent: Cannot create a component in an invalid context" ); |
913 | return nullptr; |
914 | } |
915 | |
916 | if (context->engine != engine) { |
917 | qWarning(msg: "QQmlComponent: Must create component in context from the same QQmlEngine" ); |
918 | return nullptr; |
919 | } |
920 | |
921 | if (state.completePending) { |
922 | qWarning(msg: "QQmlComponent: Cannot create new component instance before completing the previous" ); |
923 | return nullptr; |
924 | } |
925 | |
926 | if (!q->isReady()) { |
927 | qWarning(msg: "QQmlComponent: Component is not ready" ); |
928 | return nullptr; |
929 | } |
930 | |
931 | // Do not create infinite recursion in object creation |
932 | static const int maxCreationDepth = 10; |
933 | if (creationDepth.localData() >= maxCreationDepth) { |
934 | qWarning(msg: "QQmlComponent: Component creation is recursing - aborting" ); |
935 | return nullptr; |
936 | } |
937 | |
938 | QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(e: engine); |
939 | |
940 | enginePriv->inProgressCreations++; |
941 | state.errors.clear(); |
942 | state.completePending = true; |
943 | |
944 | enginePriv->referenceScarceResources(); |
945 | QObject *rv = nullptr; |
946 | state.creator.reset(other: new QQmlObjectCreator(context, compilationUnit, creationContext)); |
947 | rv = state.creator->create(subComponentIndex: start); |
948 | if (!rv) |
949 | state.errors = state.creator->errors; |
950 | enginePriv->dereferenceScarceResources(); |
951 | |
952 | if (rv) { |
953 | QQmlData *ddata = QQmlData::get(object: rv); |
954 | Q_ASSERT(ddata); |
955 | //top level objects should never get JS ownership. |
956 | //if JS ownership is needed this needs to be explicitly undone (like in component.createObject()) |
957 | ddata->indestructible = true; |
958 | ddata->explicitIndestructibleSet = true; |
959 | ddata->rootObjectInCreation = false; |
960 | } |
961 | |
962 | return rv; |
963 | } |
964 | |
965 | void QQmlComponentPrivate::beginDeferred(QQmlEnginePrivate *enginePriv, |
966 | QObject *object, DeferredState *deferredState) |
967 | { |
968 | QQmlData *ddata = QQmlData::get(object); |
969 | Q_ASSERT(!ddata->deferredData.isEmpty()); |
970 | |
971 | deferredState->constructionStates.reserve(size: ddata->deferredData.size()); |
972 | |
973 | for (QQmlData::DeferredData *deferredData : qAsConst(t&: ddata->deferredData)) { |
974 | enginePriv->inProgressCreations++; |
975 | |
976 | ConstructionState *state = new ConstructionState; |
977 | state->completePending = true; |
978 | |
979 | QQmlContextData *creationContext = nullptr; |
980 | state->creator.reset(other: new QQmlObjectCreator(deferredData->context->parent, deferredData->compilationUnit, creationContext)); |
981 | |
982 | if (!state->creator->populateDeferredProperties(instance: object, deferredData)) |
983 | state->errors << state->creator->errors; |
984 | deferredData->bindings.clear(); |
985 | |
986 | deferredState->constructionStates += state; |
987 | } |
988 | } |
989 | |
990 | void QQmlComponentPrivate::completeDeferred(QQmlEnginePrivate *enginePriv, QQmlComponentPrivate::DeferredState *deferredState) |
991 | { |
992 | for (ConstructionState *state : qAsConst(t&: deferredState->constructionStates)) |
993 | complete(enginePriv, state); |
994 | } |
995 | |
996 | void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionState *state) |
997 | { |
998 | if (state->completePending) { |
999 | QQmlInstantiationInterrupt interrupt; |
1000 | state->creator->finalize(interrupt); |
1001 | |
1002 | state->completePending = false; |
1003 | |
1004 | enginePriv->inProgressCreations--; |
1005 | |
1006 | if (0 == enginePriv->inProgressCreations) { |
1007 | while (enginePriv->erroredBindings) { |
1008 | enginePriv->warning(enginePriv->erroredBindings->removeError()); |
1009 | } |
1010 | } |
1011 | } |
1012 | } |
1013 | |
1014 | /*! |
1015 | * \internal |
1016 | * Finds the matching toplevel property with name \a name of the component \a createdComponent. |
1017 | * If it was a required property or an alias to a required property contained in \a |
1018 | * requiredProperties, it is removed from it. |
1019 | * |
1020 | * If wasInRequiredProperties is non-null, the referenced boolean is set to true iff the property |
1021 | * was found in requiredProperties. |
1022 | * |
1023 | * Returns the QQmlProperty with name \a name (which might be invalid if there is no such property), |
1024 | * for further processing (for instance, actually setting the property value). |
1025 | * |
1026 | * Note: This method is used in QQmlComponent and QQmlIncubator to manage required properties. Most |
1027 | * classes which create components should not need it and should only need to call |
1028 | * setInitialProperties. |
1029 | */ |
1030 | QQmlProperty QQmlComponentPrivate::removePropertyFromRequired(QObject *createdComponent, const QString &name, RequiredProperties &requiredProperties, bool* wasInRequiredProperties) |
1031 | { |
1032 | QQmlProperty prop(createdComponent, name); |
1033 | auto privProp = QQmlPropertyPrivate::get(p: prop); |
1034 | if (prop.isValid()) { |
1035 | // resolve outstanding required properties |
1036 | auto targetProp = &privProp->core; |
1037 | if (targetProp->isAlias()) { |
1038 | auto target = createdComponent; |
1039 | QQmlPropertyIndex originalIndex(targetProp->coreIndex()); |
1040 | QQmlPropertyIndex propIndex; |
1041 | QQmlPropertyPrivate::findAliasTarget(target, originalIndex, &target, &propIndex); |
1042 | QQmlData *data = QQmlData::get(object: target); |
1043 | Q_ASSERT(data && data->propertyCache); |
1044 | targetProp = data->propertyCache->property(index: propIndex.coreIndex()); |
1045 | } else { |
1046 | // we need to get the pointer from the property cache instead of directly using |
1047 | // targetProp else the lookup will fail |
1048 | QQmlData *data = QQmlData::get(object: createdComponent); |
1049 | Q_ASSERT(data && data->propertyCache); |
1050 | targetProp = data->propertyCache->property(index: targetProp->coreIndex()); |
1051 | } |
1052 | auto it = requiredProperties.find(key: targetProp); |
1053 | if (it != requiredProperties.end()) { |
1054 | if (wasInRequiredProperties) |
1055 | *wasInRequiredProperties = true; |
1056 | requiredProperties.erase(it); |
1057 | } else { |
1058 | if (wasInRequiredProperties) |
1059 | *wasInRequiredProperties = false; |
1060 | } |
1061 | } |
1062 | return prop; |
1063 | } |
1064 | |
1065 | /*! |
1066 | This method provides advanced control over component instance creation. |
1067 | In general, programmers should use QQmlComponent::create() to create a |
1068 | component. |
1069 | |
1070 | This function completes the component creation begun with QQmlComponent::beginCreate() |
1071 | and must be called afterwards. |
1072 | |
1073 | \sa beginCreate() |
1074 | */ |
1075 | void QQmlComponent::completeCreate() |
1076 | { |
1077 | Q_D(QQmlComponent); |
1078 | |
1079 | d->completeCreate(); |
1080 | } |
1081 | |
1082 | void QQmlComponentPrivate::completeCreate() |
1083 | { |
1084 | const RequiredProperties& unsetRequiredProperties = requiredProperties(); |
1085 | for (const auto& unsetRequiredProperty: unsetRequiredProperties) { |
1086 | QQmlError error = unsetRequiredPropertyToQQmlError(unsetRequiredProperty); |
1087 | state.errors.push_back(t: error); |
1088 | } |
1089 | if (state.completePending) { |
1090 | ++creationDepth.localData(); |
1091 | QQmlEnginePrivate *ep = QQmlEnginePrivate::get(e: engine); |
1092 | complete(enginePriv: ep, state: &state); |
1093 | --creationDepth.localData(); |
1094 | } |
1095 | } |
1096 | |
1097 | QQmlComponentAttached::QQmlComponentAttached(QObject *parent) |
1098 | : QObject(parent), prev(nullptr), next(nullptr) |
1099 | { |
1100 | } |
1101 | |
1102 | QQmlComponentAttached::~QQmlComponentAttached() |
1103 | { |
1104 | if (prev) *prev = next; |
1105 | if (next) next->prev = prev; |
1106 | prev = nullptr; |
1107 | next = nullptr; |
1108 | } |
1109 | |
1110 | /*! |
1111 | \internal |
1112 | */ |
1113 | QQmlComponentAttached *QQmlComponent::qmlAttachedProperties(QObject *obj) |
1114 | { |
1115 | QQmlComponentAttached *a = new QQmlComponentAttached(obj); |
1116 | |
1117 | QQmlEngine *engine = qmlEngine(obj); |
1118 | if (!engine) |
1119 | return a; |
1120 | |
1121 | QQmlEnginePrivate *p = QQmlEnginePrivate::get(e: engine); |
1122 | if (p->activeObjectCreator) { // XXX should only be allowed during begin |
1123 | a->add(a: p->activeObjectCreator->componentAttachment()); |
1124 | } else { |
1125 | QQmlData *d = QQmlData::get(object: obj); |
1126 | Q_ASSERT(d); |
1127 | Q_ASSERT(d->context); |
1128 | a->add(a: &d->context->componentAttached); |
1129 | } |
1130 | |
1131 | return a; |
1132 | } |
1133 | |
1134 | /*! |
1135 | Create an object instance from this component using the provided |
1136 | \a incubator. \a context specifies the context within which to create the object |
1137 | instance. |
1138 | |
1139 | If \a context is 0 (the default), it will create the instance in the |
1140 | engine's \l {QQmlEngine::rootContext()}{root context}. |
1141 | |
1142 | \a forContext specifies a context that this object creation depends upon. |
1143 | If the \a forContext is being created asynchronously, and the |
1144 | \l QQmlIncubator::IncubationMode is \l QQmlIncubator::AsynchronousIfNested, |
1145 | this object will also be created asynchronously. If \a forContext is 0 |
1146 | (the default), the \a context will be used for this decision. |
1147 | |
1148 | The created object and its creation status are available via the |
1149 | \a incubator. |
1150 | |
1151 | \sa QQmlIncubator |
1152 | */ |
1153 | |
1154 | void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context, |
1155 | QQmlContext *forContext) |
1156 | { |
1157 | Q_D(QQmlComponent); |
1158 | |
1159 | if (!context) |
1160 | context = d->engine->rootContext(); |
1161 | |
1162 | QQmlContextData *contextData = QQmlContextData::get(context); |
1163 | QQmlContextData *forContextData = contextData; |
1164 | if (forContext) forContextData = QQmlContextData::get(context: forContext); |
1165 | |
1166 | if (!contextData->isValid()) { |
1167 | qWarning(msg: "QQmlComponent: Cannot create a component in an invalid context" ); |
1168 | return; |
1169 | } |
1170 | |
1171 | if (contextData->engine != d->engine) { |
1172 | qWarning(msg: "QQmlComponent: Must create component in context from the same QQmlEngine" ); |
1173 | return; |
1174 | } |
1175 | |
1176 | if (!isReady()) { |
1177 | qWarning(msg: "QQmlComponent: Component is not ready" ); |
1178 | return; |
1179 | } |
1180 | |
1181 | incubator.clear(); |
1182 | QExplicitlySharedDataPointer<QQmlIncubatorPrivate> p(incubator.d); |
1183 | |
1184 | QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(e: d->engine); |
1185 | |
1186 | p->compilationUnit = d->compilationUnit; |
1187 | p->enginePriv = enginePriv; |
1188 | p->creator.reset(other: new QQmlObjectCreator(contextData, d->compilationUnit, d->creationContext, p.data())); |
1189 | p->subComponentToCreate = d->start; |
1190 | |
1191 | enginePriv->incubate(incubator, forContextData); |
1192 | } |
1193 | |
1194 | /*! |
1195 | Set toplevel \a properties of the \a component. |
1196 | |
1197 | |
1198 | This method provides advanced control over component instance creation. |
1199 | In general, programmers should use |
1200 | \l QQmlComponent::createWithInitialProperties to create a component. |
1201 | |
1202 | Use this method after beginCreate and before completeCreate has been called. |
1203 | If a provided property does not exist, a warning is issued. |
1204 | |
1205 | \since 5.14 |
1206 | */ |
1207 | void QQmlComponent::setInitialProperties(QObject *component, const QVariantMap &properties) |
1208 | { |
1209 | Q_D(QQmlComponent); |
1210 | for (auto it = properties.constBegin(); it != properties.constEnd(); ++it) |
1211 | d->setInitialProperty(component, name: it.key(), value: it.value()); |
1212 | } |
1213 | |
1214 | /* |
1215 | This is essentially a copy of QQmlComponent::create(); except it takes the QQmlContextData |
1216 | arguments instead of QQmlContext which means we don't have to construct the rather weighty |
1217 | wrapper class for every delegate item. |
1218 | |
1219 | This is used by QQmlDelegateModel. |
1220 | */ |
1221 | void QQmlComponentPrivate::incubateObject( |
1222 | QQmlIncubator *incubationTask, |
1223 | QQmlComponent *component, |
1224 | QQmlEngine *engine, |
1225 | QQmlContextData *context, |
1226 | QQmlContextData *forContext) |
1227 | { |
1228 | QQmlIncubatorPrivate *incubatorPriv = QQmlIncubatorPrivate::get(incubator: incubationTask); |
1229 | QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(e: engine); |
1230 | QQmlComponentPrivate *componentPriv = QQmlComponentPrivate::get(c: component); |
1231 | |
1232 | incubatorPriv->compilationUnit = componentPriv->compilationUnit; |
1233 | incubatorPriv->enginePriv = enginePriv; |
1234 | incubatorPriv->creator.reset(other: new QQmlObjectCreator(context, componentPriv->compilationUnit, componentPriv->creationContext)); |
1235 | incubatorPriv->subComponentToCreate = componentPriv->start; |
1236 | |
1237 | enginePriv->incubate(*incubationTask, forContext); |
1238 | } |
1239 | |
1240 | |
1241 | |
1242 | class QQmlComponentIncubator; |
1243 | |
1244 | namespace QV4 { |
1245 | |
1246 | namespace Heap { |
1247 | |
1248 | #define QmlIncubatorObjectMembers(class, Member) \ |
1249 | Member(class, HeapValue, HeapValue, valuemap) \ |
1250 | Member(class, HeapValue, HeapValue, statusChanged) \ |
1251 | Member(class, Pointer, QmlContext *, qmlContext) \ |
1252 | Member(class, NoMark, QQmlComponentIncubator *, incubator) \ |
1253 | Member(class, NoMark, QQmlQPointer<QObject>, parent) |
1254 | |
1255 | DECLARE_HEAP_OBJECT(QmlIncubatorObject, Object) { |
1256 | DECLARE_MARKOBJECTS(QmlIncubatorObject); |
1257 | |
1258 | void init(QQmlIncubator::IncubationMode = QQmlIncubator::Asynchronous); |
1259 | inline void destroy(); |
1260 | }; |
1261 | |
1262 | } |
1263 | |
1264 | struct QmlIncubatorObject : public QV4::Object |
1265 | { |
1266 | V4_OBJECT2(QmlIncubatorObject, Object) |
1267 | V4_NEEDS_DESTROY |
1268 | |
1269 | static ReturnedValue method_get_statusChanged(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); |
1270 | static ReturnedValue method_set_statusChanged(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); |
1271 | static ReturnedValue method_get_status(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); |
1272 | static ReturnedValue method_get_object(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); |
1273 | static ReturnedValue method_forceCompletion(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); |
1274 | |
1275 | void statusChanged(QQmlIncubator::Status); |
1276 | void setInitialState(QObject *, RequiredProperties &requiredProperties); |
1277 | }; |
1278 | |
1279 | } |
1280 | |
1281 | DEFINE_OBJECT_VTABLE(QV4::QmlIncubatorObject); |
1282 | |
1283 | class QQmlComponentIncubator : public QQmlIncubator |
1284 | { |
1285 | public: |
1286 | QQmlComponentIncubator(QV4::Heap::QmlIncubatorObject *inc, IncubationMode mode) |
1287 | : QQmlIncubator(mode) |
1288 | { |
1289 | incubatorObject.set(engine: inc->internalClass->engine, obj: inc); |
1290 | } |
1291 | |
1292 | void statusChanged(Status s) override { |
1293 | QV4::Scope scope(incubatorObject.engine()); |
1294 | QV4::Scoped<QV4::QmlIncubatorObject> i(scope, incubatorObject.as<QV4::QmlIncubatorObject>()); |
1295 | i->statusChanged(s); |
1296 | } |
1297 | |
1298 | void setInitialState(QObject *o) override { |
1299 | QV4::Scope scope(incubatorObject.engine()); |
1300 | QV4::Scoped<QV4::QmlIncubatorObject> i(scope, incubatorObject.as<QV4::QmlIncubatorObject>()); |
1301 | auto d = QQmlIncubatorPrivate::get(incubator: this); |
1302 | i->setInitialState(o, requiredProperties&: d->requiredProperties()); |
1303 | } |
1304 | |
1305 | QV4::PersistentValue incubatorObject; // keep a strong internal reference while incubating |
1306 | }; |
1307 | |
1308 | |
1309 | static void QQmlComponent_setQmlParent(QObject *me, QObject *parent) |
1310 | { |
1311 | if (parent) { |
1312 | me->setParent(parent); |
1313 | typedef QQmlPrivate::AutoParentFunction APF; |
1314 | QList<APF> functions = QQmlMetaType::parentFunctions(); |
1315 | |
1316 | bool needParent = false; |
1317 | for (int ii = 0; ii < functions.count(); ++ii) { |
1318 | QQmlPrivate::AutoParentResult res = functions.at(i: ii)(me, parent); |
1319 | if (res == QQmlPrivate::Parented) { |
1320 | needParent = false; |
1321 | break; |
1322 | } else if (res == QQmlPrivate::IncompatibleParent) { |
1323 | needParent = true; |
1324 | } |
1325 | } |
1326 | if (needParent) |
1327 | qWarning(msg: "QQmlComponent: Created graphical object was not " |
1328 | "placed in the graphics scene." ); |
1329 | } |
1330 | } |
1331 | |
1332 | /*! |
1333 | \qmlmethod object Component::createObject(QtObject parent, object properties) |
1334 | |
1335 | Creates and returns an object instance of this component that will have |
1336 | the given \a parent and \a properties. The \a properties argument is optional. |
1337 | Returns null if object creation fails. |
1338 | |
1339 | The object will be created in the same context as the one in which the component |
1340 | was created. This function will always return null when called on components |
1341 | which were not created in QML. |
1342 | |
1343 | If you wish to create an object without setting a parent, specify \c null for |
1344 | the \a parent value. Note that if the returned object is to be displayed, you |
1345 | must provide a valid \a parent value or set the returned object's \l{Item::parent}{parent} |
1346 | property, otherwise the object will not be visible. |
1347 | |
1348 | If a \a parent is not provided to createObject(), a reference to the returned object must be held so that |
1349 | it is not destroyed by the garbage collector. This is true regardless of whether \l{Item::parent} is set afterwards, |
1350 | because setting the Item parent does not change object ownership. Only the graphical parent is changed. |
1351 | |
1352 | As of \c {QtQuick 1.1}, this method accepts an optional \a properties argument that specifies a |
1353 | map of initial property values for the created object. These values are applied before the object |
1354 | creation is finalized. This is more efficient than setting property values after object creation, |
1355 | particularly where large sets of property values are defined, and also allows property bindings |
1356 | to be set up (using \l{Qt::binding}{Qt.binding}) before the object is created. |
1357 | |
1358 | The \a properties argument is specified as a map of property-value items. For example, the code |
1359 | below creates an object with initial \c x and \c y values of 100 and 100, respectively: |
1360 | |
1361 | \js |
1362 | var component = Qt.createComponent("Button.qml"); |
1363 | if (component.status == Component.Ready) |
1364 | component.createObject(parent, {x: 100, y: 100}); |
1365 | \endjs |
1366 | |
1367 | Dynamically created instances can be deleted with the \c destroy() method. |
1368 | See \l {Dynamic QML Object Creation from JavaScript} for more information. |
1369 | |
1370 | \sa incubateObject() |
1371 | */ |
1372 | |
1373 | |
1374 | void QQmlComponentPrivate::setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v, RequiredProperties &requiredProperties, QObject *createdComponent) |
1375 | { |
1376 | QV4::Scope scope(engine); |
1377 | QV4::ScopedObject object(scope); |
1378 | QV4::ScopedObject valueMap(scope, v); |
1379 | QV4::ObjectIterator it(scope, valueMap, QV4::ObjectIterator::EnumerableOnly); |
1380 | QV4::ScopedString name(scope); |
1381 | QV4::ScopedValue val(scope); |
1382 | if (engine->hasException) |
1383 | return; |
1384 | |
1385 | QV4::ScopedStackFrame frame(scope, qmlContext->d()); |
1386 | |
1387 | while (1) { |
1388 | name = it.nextPropertyNameAsString(value: val); |
1389 | if (!name) |
1390 | break; |
1391 | object = o; |
1392 | const QStringList properties = name->toQString().split(sep: QLatin1Char('.')); |
1393 | bool isTopLevelProperty = properties.size() == 1; |
1394 | for (int i = 0; i < properties.length() - 1; ++i) { |
1395 | name = engine->newString(s: properties.at(i)); |
1396 | object = object->get(name); |
1397 | if (engine->hasException || !object) { |
1398 | break; |
1399 | } |
1400 | } |
1401 | if (engine->hasException || !object) { |
1402 | engine->hasException = false; |
1403 | continue; |
1404 | } |
1405 | name = engine->newString(s: properties.last()); |
1406 | object->put(name, v: val); |
1407 | if (engine->hasException) { |
1408 | engine->hasException = false; |
1409 | continue; |
1410 | } else if (isTopLevelProperty) { |
1411 | auto prop = removePropertyFromRequired(createdComponent, name: name->toQString(), requiredProperties); |
1412 | } |
1413 | } |
1414 | |
1415 | engine->hasException = false; |
1416 | } |
1417 | |
1418 | QQmlError QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(const RequiredPropertyInfo &unsetRequiredProperty) |
1419 | { |
1420 | QQmlError error; |
1421 | QString description = QLatin1String("Required property %1 was not initialized" ).arg(args: unsetRequiredProperty.propertyName); |
1422 | switch (unsetRequiredProperty.aliasesToRequired.size()) { |
1423 | case 0: |
1424 | break; |
1425 | case 1: { |
1426 | const auto info = unsetRequiredProperty.aliasesToRequired.first(); |
1427 | description += QLatin1String("\nIt can be set via the alias property %1 from %2\n" ).arg(args: info.propertyName, args: info.fileUrl.toString()); |
1428 | break; |
1429 | } |
1430 | default: |
1431 | description += QLatin1String("\nIt can be set via one of the following alias properties:" ); |
1432 | for (auto aliasInfo: unsetRequiredProperty.aliasesToRequired) { |
1433 | description += QLatin1String("\n- %1 (%2)" ).arg(args&: aliasInfo.propertyName, args: aliasInfo.fileUrl.toString()); |
1434 | } |
1435 | description += QLatin1Char('\n'); |
1436 | } |
1437 | error.setDescription(description); |
1438 | error.setUrl(unsetRequiredProperty.fileUrl); |
1439 | error.setLine(qmlConvertSourceCoordinate<quint32, int>(n: unsetRequiredProperty.location.line)); |
1440 | error.setColumn(qmlConvertSourceCoordinate<quint32, int>(n: unsetRequiredProperty.location.column)); |
1441 | return error; |
1442 | } |
1443 | |
1444 | /*! |
1445 | \internal |
1446 | */ |
1447 | void QQmlComponent::createObject(QQmlV4Function *args) |
1448 | { |
1449 | Q_D(QQmlComponent); |
1450 | Q_ASSERT(d->engine); |
1451 | Q_ASSERT(args); |
1452 | |
1453 | QObject *parent = nullptr; |
1454 | QV4::ExecutionEngine *v4 = args->v4engine(); |
1455 | QV4::Scope scope(v4); |
1456 | QV4::ScopedValue valuemap(scope, QV4::Value::undefinedValue()); |
1457 | |
1458 | if (args->length() >= 1) { |
1459 | QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, (*args)[0]); |
1460 | if (qobjectWrapper) |
1461 | parent = qobjectWrapper->object(); |
1462 | } |
1463 | |
1464 | if (args->length() >= 2) { |
1465 | QV4::ScopedValue v(scope, (*args)[1]); |
1466 | if (!v->as<QV4::Object>() || v->as<QV4::ArrayObject>()) { |
1467 | qmlWarning(me: this) << tr(s: "createObject: value is not an object" ); |
1468 | args->setReturnValue(QV4::Encode::null()); |
1469 | return; |
1470 | } |
1471 | valuemap = v; |
1472 | } |
1473 | |
1474 | QQmlContext *ctxt = creationContext(); |
1475 | if (!ctxt) ctxt = d->engine->rootContext(); |
1476 | |
1477 | QObject *rv = beginCreate(publicContext: ctxt); |
1478 | |
1479 | if (!rv) { |
1480 | args->setReturnValue(QV4::Encode::null()); |
1481 | return; |
1482 | } |
1483 | |
1484 | QQmlComponent_setQmlParent(me: rv, parent); |
1485 | |
1486 | QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(engine: v4, object: rv)); |
1487 | Q_ASSERT(object->isObject()); |
1488 | |
1489 | if (!valuemap->isUndefined()) { |
1490 | QV4::Scoped<QV4::QmlContext> qmlContext(scope, v4->qmlContext()); |
1491 | QQmlComponentPrivate::setInitialProperties(engine: v4, qmlContext, o: object, v: valuemap, requiredProperties&: d->requiredProperties(), createdComponent: rv); |
1492 | } |
1493 | if (!d->requiredProperties().empty()) { |
1494 | QList<QQmlError> errors; |
1495 | for (const auto &requiredProperty: d->requiredProperties()) { |
1496 | errors.push_back(t: QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(unsetRequiredProperty: requiredProperty)); |
1497 | } |
1498 | qmlWarning(me: rv, errors); |
1499 | args->setReturnValue(QV4::Encode::null()); |
1500 | delete rv; |
1501 | return; |
1502 | } |
1503 | |
1504 | d->completeCreate(); |
1505 | |
1506 | Q_ASSERT(QQmlData::get(rv)); |
1507 | QQmlData::get(object: rv)->explicitIndestructibleSet = false; |
1508 | QQmlData::get(object: rv)->indestructible = false; |
1509 | |
1510 | args->setReturnValue(object->asReturnedValue()); |
1511 | } |
1512 | |
1513 | /*! |
1514 | \qmlmethod object Component::incubateObject(Item parent, object properties, enumeration mode) |
1515 | |
1516 | Creates an incubator for an instance of this component. Incubators allow new component |
1517 | instances to be instantiated asynchronously and do not cause freezes in the UI. |
1518 | |
1519 | The \a parent argument specifies the parent the created instance will have. Omitting the |
1520 | parameter or passing null will create an object with no parent. In this case, a reference |
1521 | to the created object must be held so that it is not destroyed by the garbage collector. |
1522 | |
1523 | The \a properties argument is specified as a map of property-value items which will be |
1524 | set on the created object during its construction. \a mode may be Qt.Synchronous or |
1525 | Qt.Asynchronous, and controls whether the instance is created synchronously or asynchronously. |
1526 | The default is asynchronous. In some circumstances, even if Qt.Synchronous is specified, |
1527 | the incubator may create the object asynchronously. This happens if the component calling |
1528 | incubateObject() is itself being created asynchronously. |
1529 | |
1530 | All three arguments are optional. |
1531 | |
1532 | If successful, the method returns an incubator, otherwise null. The incubator has the following |
1533 | properties: |
1534 | |
1535 | \list |
1536 | \li status The status of the incubator. Valid values are Component.Ready, Component.Loading and |
1537 | Component.Error. |
1538 | \li object The created object instance. Will only be available once the incubator is in the |
1539 | Ready status. |
1540 | \li onStatusChanged Specifies a callback function to be invoked when the status changes. The |
1541 | status is passed as a parameter to the callback. |
1542 | \li forceCompletion() Call to complete incubation synchronously. |
1543 | \endlist |
1544 | |
1545 | The following example demonstrates how to use an incubator: |
1546 | |
1547 | \js |
1548 | var component = Qt.createComponent("Button.qml"); |
1549 | |
1550 | var incubator = component.incubateObject(parent, { x: 10, y: 10 }); |
1551 | if (incubator.status != Component.Ready) { |
1552 | incubator.onStatusChanged = function(status) { |
1553 | if (status == Component.Ready) { |
1554 | print ("Object", incubator.object, "is now ready!"); |
1555 | } |
1556 | } |
1557 | } else { |
1558 | print ("Object", incubator.object, "is ready immediately!"); |
1559 | } |
1560 | \endjs |
1561 | |
1562 | Dynamically created instances can be deleted with the \c destroy() method. |
1563 | See \l {Dynamic QML Object Creation from JavaScript} for more information. |
1564 | |
1565 | \sa createObject() |
1566 | */ |
1567 | |
1568 | /*! |
1569 | \internal |
1570 | */ |
1571 | void QQmlComponent::incubateObject(QQmlV4Function *args) |
1572 | { |
1573 | Q_D(QQmlComponent); |
1574 | Q_ASSERT(d->engine); |
1575 | Q_UNUSED(d); |
1576 | Q_ASSERT(args); |
1577 | QV4::ExecutionEngine *v4 = args->v4engine(); |
1578 | QV4::Scope scope(v4); |
1579 | |
1580 | QObject *parent = nullptr; |
1581 | QV4::ScopedValue valuemap(scope, QV4::Value::undefinedValue()); |
1582 | QQmlIncubator::IncubationMode mode = QQmlIncubator::Asynchronous; |
1583 | |
1584 | if (args->length() >= 1) { |
1585 | QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, (*args)[0]); |
1586 | if (qobjectWrapper) |
1587 | parent = qobjectWrapper->object(); |
1588 | } |
1589 | |
1590 | if (args->length() >= 2) { |
1591 | QV4::ScopedValue v(scope, (*args)[1]); |
1592 | if (v->isNull()) { |
1593 | } else if (!v->as<QV4::Object>() || v->as<QV4::ArrayObject>()) { |
1594 | qmlWarning(me: this) << tr(s: "createObject: value is not an object" ); |
1595 | args->setReturnValue(QV4::Encode::null()); |
1596 | return; |
1597 | } else { |
1598 | valuemap = v; |
1599 | } |
1600 | } |
1601 | |
1602 | if (args->length() >= 3) { |
1603 | QV4::ScopedValue val(scope, (*args)[2]); |
1604 | quint32 v = val->toUInt32(); |
1605 | if (v == 0) |
1606 | mode = QQmlIncubator::Asynchronous; |
1607 | else if (v == 1) |
1608 | mode = QQmlIncubator::AsynchronousIfNested; |
1609 | } |
1610 | |
1611 | QQmlComponentExtension *e = componentExtension(engine: args->v4engine()); |
1612 | |
1613 | QV4::Scoped<QV4::QmlIncubatorObject> r(scope, v4->memoryManager->allocate<QV4::QmlIncubatorObject>(args: mode)); |
1614 | QV4::ScopedObject p(scope, e->incubationProto.value()); |
1615 | r->setPrototypeOf(p); |
1616 | |
1617 | if (!valuemap->isUndefined()) |
1618 | r->d()->valuemap.set(e: scope.engine, newVal: valuemap); |
1619 | r->d()->qmlContext.set(e: scope.engine, newVal: v4->qmlContext()); |
1620 | r->d()->parent = parent; |
1621 | |
1622 | QQmlIncubator *incubator = r->d()->incubator; |
1623 | create(incubator&: *incubator, context: creationContext()); |
1624 | |
1625 | if (incubator->status() == QQmlIncubator::Null) { |
1626 | args->setReturnValue(QV4::Encode::null()); |
1627 | } else { |
1628 | args->setReturnValue(r.asReturnedValue()); |
1629 | } |
1630 | } |
1631 | |
1632 | // XXX used by QSGLoader |
1633 | void QQmlComponentPrivate::initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate, RequiredProperties &requiredProperties) |
1634 | { |
1635 | QV4::ExecutionEngine *v4engine = engine->handle(); |
1636 | QV4::Scope scope(v4engine); |
1637 | |
1638 | QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(engine: v4engine, object: toCreate)); |
1639 | Q_ASSERT(object->as<QV4::Object>()); |
1640 | |
1641 | if (!valuemap.isUndefined()) |
1642 | setInitialProperties(engine: v4engine, qmlContext, o: object, v: valuemap, requiredProperties, createdComponent: toCreate); |
1643 | } |
1644 | |
1645 | QQmlComponentExtension::QQmlComponentExtension(QV4::ExecutionEngine *v4) |
1646 | { |
1647 | QV4::Scope scope(v4); |
1648 | QV4::ScopedObject proto(scope, v4->newObject()); |
1649 | proto->defineAccessorProperty(QStringLiteral("onStatusChanged" ), |
1650 | getter: QV4::QmlIncubatorObject::method_get_statusChanged, setter: QV4::QmlIncubatorObject::method_set_statusChanged); |
1651 | proto->defineAccessorProperty(QStringLiteral("status" ), getter: QV4::QmlIncubatorObject::method_get_status, setter: nullptr); |
1652 | proto->defineAccessorProperty(QStringLiteral("object" ), getter: QV4::QmlIncubatorObject::method_get_object, setter: nullptr); |
1653 | proto->defineDefaultProperty(QStringLiteral("forceCompletion" ), code: QV4::QmlIncubatorObject::method_forceCompletion); |
1654 | |
1655 | incubationProto.set(engine: v4, value: proto); |
1656 | } |
1657 | |
1658 | QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_object(const FunctionObject *b, const Value *thisObject, const Value *, int) |
1659 | { |
1660 | QV4::Scope scope(b); |
1661 | QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>()); |
1662 | if (!o) |
1663 | THROW_TYPE_ERROR(); |
1664 | |
1665 | return QV4::QObjectWrapper::wrap(engine: scope.engine, object: o->d()->incubator->object()); |
1666 | } |
1667 | |
1668 | QV4::ReturnedValue QV4::QmlIncubatorObject::method_forceCompletion(const FunctionObject *b, const Value *thisObject, const Value *, int) |
1669 | { |
1670 | QV4::Scope scope(b); |
1671 | QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>()); |
1672 | if (!o) |
1673 | THROW_TYPE_ERROR(); |
1674 | |
1675 | o->d()->incubator->forceCompletion(); |
1676 | |
1677 | RETURN_UNDEFINED(); |
1678 | } |
1679 | |
1680 | QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_status(const FunctionObject *b, const Value *thisObject, const Value *, int) |
1681 | { |
1682 | QV4::Scope scope(b); |
1683 | QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>()); |
1684 | if (!o) |
1685 | THROW_TYPE_ERROR(); |
1686 | |
1687 | return QV4::Encode(o->d()->incubator->status()); |
1688 | } |
1689 | |
1690 | QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_statusChanged(const FunctionObject *b, const Value *thisObject, const Value *, int) |
1691 | { |
1692 | QV4::Scope scope(b); |
1693 | QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>()); |
1694 | if (!o) |
1695 | THROW_TYPE_ERROR(); |
1696 | |
1697 | return QV4::Encode(o->d()->statusChanged); |
1698 | } |
1699 | |
1700 | QV4::ReturnedValue QV4::QmlIncubatorObject::method_set_statusChanged(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
1701 | { |
1702 | QV4::Scope scope(b); |
1703 | QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>()); |
1704 | if (!o || argc < 1) |
1705 | THROW_TYPE_ERROR(); |
1706 | |
1707 | o->d()->statusChanged.set(e: scope.engine, newVal: argv[0]); |
1708 | |
1709 | RETURN_UNDEFINED(); |
1710 | } |
1711 | |
1712 | QQmlComponentExtension::~QQmlComponentExtension() |
1713 | { |
1714 | } |
1715 | |
1716 | void QV4::Heap::QmlIncubatorObject::init(QQmlIncubator::IncubationMode m) |
1717 | { |
1718 | Object::init(); |
1719 | valuemap.set(e: internalClass->engine, newVal: QV4::Value::undefinedValue()); |
1720 | statusChanged.set(e: internalClass->engine, newVal: QV4::Value::undefinedValue()); |
1721 | parent.init(); |
1722 | qmlContext.set(e: internalClass->engine, newVal: nullptr); |
1723 | incubator = new QQmlComponentIncubator(this, m); |
1724 | } |
1725 | |
1726 | void QV4::Heap::QmlIncubatorObject::destroy() { |
1727 | delete incubator; |
1728 | parent.destroy(); |
1729 | Object::destroy(); |
1730 | } |
1731 | |
1732 | void QV4::QmlIncubatorObject::setInitialState(QObject *o, RequiredProperties &requiredProperties) |
1733 | { |
1734 | QQmlComponent_setQmlParent(me: o, parent: d()->parent); |
1735 | |
1736 | if (!d()->valuemap.isUndefined()) { |
1737 | QV4::ExecutionEngine *v4 = engine(); |
1738 | QV4::Scope scope(v4); |
1739 | QV4::ScopedObject obj(scope, QV4::QObjectWrapper::wrap(engine: v4, object: o)); |
1740 | QV4::Scoped<QV4::QmlContext> qmlCtxt(scope, d()->qmlContext); |
1741 | QQmlComponentPrivate::setInitialProperties(engine: v4, qmlContext: qmlCtxt, o: obj, v: d()->valuemap, requiredProperties, createdComponent: o); |
1742 | } |
1743 | } |
1744 | |
1745 | void QV4::QmlIncubatorObject::statusChanged(QQmlIncubator::Status s) |
1746 | { |
1747 | QV4::Scope scope(engine()); |
1748 | // hold the incubated object in a scoped value to prevent it's destruction before this method returns |
1749 | QV4::ScopedObject incubatedObject(scope, QV4::QObjectWrapper::wrap(engine: scope.engine, object: d()->incubator->object())); |
1750 | |
1751 | if (s == QQmlIncubator::Ready) { |
1752 | Q_ASSERT(QQmlData::get(d()->incubator->object())); |
1753 | QQmlData::get(object: d()->incubator->object())->explicitIndestructibleSet = false; |
1754 | QQmlData::get(object: d()->incubator->object())->indestructible = false; |
1755 | } |
1756 | |
1757 | QV4::ScopedFunctionObject f(scope, d()->statusChanged); |
1758 | if (f) { |
1759 | QV4::JSCallData jsCallData(scope, 1); |
1760 | *jsCallData->thisObject = this; |
1761 | jsCallData->args[0] = QV4::Value::fromUInt32(i: s); |
1762 | f->call(data: jsCallData); |
1763 | if (scope.hasException()) { |
1764 | QQmlError error = scope.engine->catchExceptionAsQmlError(); |
1765 | QQmlEnginePrivate::warning(QQmlEnginePrivate::get(e: scope.engine->qmlEngine()), error); |
1766 | } |
1767 | } |
1768 | |
1769 | if (s != QQmlIncubator::Loading) |
1770 | d()->incubator->incubatorObject.clear(); |
1771 | } |
1772 | |
1773 | #undef INITIALPROPERTIES_SOURCE |
1774 | |
1775 | QT_END_NAMESPACE |
1776 | |
1777 | #include "moc_qqmlcomponent.cpp" |
1778 | |