1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the QtDeclarative 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 Digia. For licensing terms and |
14 | ** conditions see http://qt.digia.com/licensing. For further information |
15 | ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 2.1 requirements |
23 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
24 | ** |
25 | ** In addition, as a special exception, Digia gives you certain additional |
26 | ** rights. These rights are described in the Digia Qt LGPL Exception |
27 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
28 | ** |
29 | ** GNU General Public License Usage |
30 | ** Alternatively, this file may be used under the terms of the GNU |
31 | ** General Public License version 3.0 as published by the Free Software |
32 | ** Foundation and appearing in the file LICENSE.GPL included in the |
33 | ** packaging of this file. Please review the following information to |
34 | ** ensure the GNU General Public License version 3.0 requirements will be |
35 | ** met: http://www.gnu.org/copyleft/gpl.html. |
36 | ** |
37 | ** |
38 | ** $QT_END_LICENSE$ |
39 | ** |
40 | ****************************************************************************/ |
41 | |
42 | #include "qdeclarativecomponent.h" |
43 | #include "private/qdeclarativecomponent_p.h" |
44 | |
45 | #include "private/qdeclarativecompiler_p.h" |
46 | #include "private/qdeclarativecontext_p.h" |
47 | #include "private/qdeclarativeengine_p.h" |
48 | #include "private/qdeclarativevme_p.h" |
49 | #include "qdeclarative.h" |
50 | #include "qdeclarativeengine.h" |
51 | #include "private/qdeclarativebinding_p.h" |
52 | #include "private/qdeclarativebinding_p_p.h" |
53 | #include "private/qdeclarativeglobal_p.h" |
54 | #include "private/qdeclarativescriptparser_p.h" |
55 | #include "private/qdeclarativedebugtrace_p.h" |
56 | #include "private/qdeclarativeenginedebugservice_p.h" |
57 | #include <QtScript/qscriptvalueiterator.h> |
58 | |
59 | #include <QStack> |
60 | #include <QStringList> |
61 | #include <QtCore/qdebug.h> |
62 | #include <QApplication> |
63 | #include <qdeclarativeinfo.h> |
64 | |
65 | QT_BEGIN_NAMESPACE |
66 | |
67 | class QByteArray; |
68 | |
69 | /*! |
70 | \class QDeclarativeComponent |
71 | \since 4.7 |
72 | \brief The QDeclarativeComponent class encapsulates a QML component definition. |
73 | \mainclass |
74 | |
75 | Components are reusable, encapsulated QML elements with well-defined interfaces. |
76 | They are often defined in \l {qdeclarativedocuments.html}{Component Files}. |
77 | |
78 | A QDeclarativeComponent instance can be created from a QML file. |
79 | For example, if there is a \c main.qml file like this: |
80 | |
81 | \qml |
82 | import QtQuick 1.0 |
83 | |
84 | Item { |
85 | width: 200 |
86 | height: 200 |
87 | } |
88 | \endqml |
89 | |
90 | The following code loads this QML file as a component, creates an instance of |
91 | this component using create(), and then queries the \l Item's \l {Item::}{width} |
92 | value: |
93 | |
94 | \code |
95 | QDeclarativeEngine *engine = new QDeclarativeEngine; |
96 | QDeclarativeComponent component(engine, QUrl::fromLocalFile("main.qml")); |
97 | |
98 | QObject *myObject = component.create(); |
99 | QDeclarativeItem *item = qobject_cast<QDeclarativeItem*>(myObject); |
100 | int width = item->width(); // width = 200 |
101 | \endcode |
102 | |
103 | |
104 | \section2 Network Components |
105 | |
106 | If the URL passed to QDeclarativeComponent is a network resource, or if the QML document references a |
107 | network resource, the QDeclarativeComponent has to fetch the network data before it is able to create |
108 | objects. In this case, the QDeclarativeComponent will have a \l {QDeclarativeComponent::Loading}{Loading} |
109 | \l {QDeclarativeComponent::status()}{status}. An application will have to wait until the component |
110 | is \l {QDeclarativeComponent::Ready}{Ready} before calling \l {QDeclarativeComponent::create()}. |
111 | |
112 | The following example shows how to load a QML file from a network resource. After creating |
113 | the QDeclarativeComponent, it tests whether the component is loading. If it is, it connects to the |
114 | QDeclarativeComponent::statusChanged() signal and otherwise calls the \c {continueLoading()} method |
115 | directly. Note that QDeclarativeComponent::isLoading() may be false for a network component if the |
116 | component has been cached and is ready immediately. |
117 | |
118 | \code |
119 | MyApplication::MyApplication() |
120 | { |
121 | // ... |
122 | component = new QDeclarativeComponent(engine, QUrl("http://www.example.com/main.qml")); |
123 | if (component->isLoading()) |
124 | QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), |
125 | this, SLOT(continueLoading())); |
126 | else |
127 | continueLoading(); |
128 | } |
129 | |
130 | void MyApplication::continueLoading() |
131 | { |
132 | if (component->isError()) { |
133 | qWarning() << component->errors(); |
134 | } else { |
135 | QObject *myObject = component->create(); |
136 | } |
137 | } |
138 | \endcode |
139 | |
140 | \sa {Using QML Bindings in C++ Applications}, {Integrating QML Code with Existing Qt UI Code} |
141 | */ |
142 | |
143 | /*! |
144 | \qmlclass Component QDeclarativeComponent |
145 | \ingroup qml-utility-elements |
146 | \since 4.7 |
147 | \brief The Component element encapsulates a QML component definition. |
148 | |
149 | Components are reusable, encapsulated QML elements with well-defined interfaces. |
150 | |
151 | Components are often defined by \l {qdeclarativedocuments.html}{component files} - |
152 | that is, \c .qml files. The \e Component element essentially allows QML components |
153 | to be defined inline, within a \l {QML Document}{QML document}, rather than as a separate QML file. |
154 | This may be useful for reusing a small component within a QML file, or for defining |
155 | a component that logically belongs with other QML components within a file. |
156 | |
157 | For example, here is a component that is used by multiple \l Loader objects. |
158 | It contains a single item, a \l Rectangle: |
159 | |
160 | \snippet doc/src/snippets/declarative/component.qml 0 |
161 | |
162 | Notice that while a \l Rectangle by itself would be automatically |
163 | rendered and displayed, this is not the case for the above rectangle |
164 | because it is defined inside a \c Component. The component encapsulates the |
165 | QML elements within, as if they were defined in a separate QML |
166 | file, and is not loaded until requested (in this case, by the |
167 | two \l Loader objects). |
168 | |
169 | Defining a \c Component is similar to defining a \l {QML Document}{QML document}. |
170 | A QML document has a single top-level item that defines the behaviors and |
171 | properties of that component, and cannot define properties or behaviors outside |
172 | of that top-level item. In the same way, a \c Component definition contains a single |
173 | top level item (which in the above example is a \l Rectangle) and cannot define any |
174 | data outside of this item, with the exception of an \e id (which in the above example |
175 | is \e redSquare). |
176 | |
177 | The \c Component element is commonly used to provide graphical components |
178 | for views. For example, the ListView::delegate property requires a \c Component |
179 | to specify how each list item is to be displayed. |
180 | |
181 | \c Component objects can also be created dynamically using |
182 | \l{QML:Qt::createComponent()}{Qt.createComponent()}. |
183 | */ |
184 | |
185 | /*! |
186 | \qmlattachedsignal Component::onCompleted() |
187 | |
188 | Emitted after component "startup" has completed. This can be used to |
189 | execute script code at startup, once the full QML environment has been |
190 | established. |
191 | |
192 | The \c {Component::onCompleted} attached property can be applied to |
193 | any element. The order of running the \c onCompleted scripts is |
194 | undefined. |
195 | |
196 | \qml |
197 | Rectangle { |
198 | Component.onCompleted: console.log("Completed Running!") |
199 | Rectangle { |
200 | Component.onCompleted: console.log("Nested Completed Running!") |
201 | } |
202 | } |
203 | \endqml |
204 | */ |
205 | |
206 | /*! |
207 | \qmlattachedsignal Component::onDestruction() |
208 | |
209 | Emitted as the component begins destruction. This can be used to undo |
210 | work done in the onCompleted signal, or other imperative code in your |
211 | application. |
212 | |
213 | The \c {Component::onDestruction} attached property can be applied to |
214 | any element. However, it applies to the destruction of the component as |
215 | a whole, and not the destruction of the specific object. The order of |
216 | running the \c onDestruction scripts is undefined. |
217 | |
218 | \qml |
219 | Rectangle { |
220 | Component.onDestruction: console.log("Destruction Beginning!") |
221 | Rectangle { |
222 | Component.onDestruction: console.log("Nested Destruction Beginning!") |
223 | } |
224 | } |
225 | \endqml |
226 | |
227 | \sa QtDeclarative |
228 | */ |
229 | |
230 | /*! |
231 | \enum QDeclarativeComponent::Status |
232 | |
233 | Specifies the loading status of the QDeclarativeComponent. |
234 | |
235 | \value Null This QDeclarativeComponent has no data. Call loadUrl() or setData() to add QML content. |
236 | \value Ready This QDeclarativeComponent is ready and create() may be called. |
237 | \value Loading This QDeclarativeComponent is loading network data. |
238 | \value Error An error has occurred. Call errors() to retrieve a list of \{QDeclarativeError}{errors}. |
239 | */ |
240 | |
241 | void QDeclarativeComponentPrivate::typeDataReady(QDeclarativeTypeData *) |
242 | { |
243 | Q_Q(QDeclarativeComponent); |
244 | |
245 | Q_ASSERT(typeData); |
246 | |
247 | fromTypeData(typeData); |
248 | typeData = 0; |
249 | |
250 | emit q->statusChanged(q->status()); |
251 | } |
252 | |
253 | void QDeclarativeComponentPrivate::typeDataProgress(QDeclarativeTypeData *, qreal p) |
254 | { |
255 | Q_Q(QDeclarativeComponent); |
256 | |
257 | progress = p; |
258 | |
259 | emit q->progressChanged(p); |
260 | } |
261 | |
262 | void QDeclarativeComponentPrivate::fromTypeData(QDeclarativeTypeData *data) |
263 | { |
264 | url = data->finalUrl(); |
265 | QDeclarativeCompiledData *c = data->compiledData(); |
266 | |
267 | if (!c) { |
268 | Q_ASSERT(data->isError()); |
269 | state.errors = data->errors(); |
270 | } else { |
271 | cc = c; |
272 | } |
273 | |
274 | data->release(); |
275 | } |
276 | |
277 | void QDeclarativeComponentPrivate::clear() |
278 | { |
279 | if (typeData) { |
280 | typeData->unregisterCallback(this); |
281 | typeData->release(); |
282 | typeData = 0; |
283 | } |
284 | |
285 | if (cc) { |
286 | cc->release(); |
287 | cc = 0; |
288 | } |
289 | } |
290 | |
291 | /*! |
292 | \internal |
293 | */ |
294 | QDeclarativeComponent::QDeclarativeComponent(QObject *parent) |
295 | : QObject(*(new QDeclarativeComponentPrivate), parent) |
296 | { |
297 | } |
298 | |
299 | /*! |
300 | Destruct the QDeclarativeComponent. |
301 | */ |
302 | QDeclarativeComponent::~QDeclarativeComponent() |
303 | { |
304 | Q_D(QDeclarativeComponent); |
305 | |
306 | if (d->state.completePending) { |
307 | qWarning("QDeclarativeComponent: Component destroyed while completion pending" ); |
308 | d->completeCreate(); |
309 | } |
310 | |
311 | if (d->typeData) { |
312 | d->typeData->unregisterCallback(d); |
313 | d->typeData->release(); |
314 | } |
315 | if (d->cc) |
316 | d->cc->release(); |
317 | } |
318 | |
319 | /*! |
320 | \qmlproperty enumeration Component::status |
321 | This property holds the status of component loading. It can be one of: |
322 | \list |
323 | \o Component.Null - no data is available for the component |
324 | \o Component.Ready - the component has been loaded, and can be used to create instances. |
325 | \o Component.Loading - the component is currently being loaded |
326 | \o Component.Error - an error occurred while loading the component. |
327 | Calling errorString() will provide a human-readable description of any errors. |
328 | \endlist |
329 | */ |
330 | |
331 | /*! |
332 | \property QDeclarativeComponent::status |
333 | The component's current \l{QDeclarativeComponent::Status} {status}. |
334 | */ |
335 | QDeclarativeComponent::Status QDeclarativeComponent::status() const |
336 | { |
337 | Q_D(const QDeclarativeComponent); |
338 | |
339 | if (d->typeData) |
340 | return Loading; |
341 | else if (!d->state.errors.isEmpty()) |
342 | return Error; |
343 | else if (d->engine && d->cc) |
344 | return Ready; |
345 | else |
346 | return Null; |
347 | } |
348 | |
349 | /*! |
350 | Returns true if status() == QDeclarativeComponent::Null. |
351 | */ |
352 | bool QDeclarativeComponent::isNull() const |
353 | { |
354 | return status() == Null; |
355 | } |
356 | |
357 | /*! |
358 | Returns true if status() == QDeclarativeComponent::Ready. |
359 | */ |
360 | bool QDeclarativeComponent::isReady() const |
361 | { |
362 | return status() == Ready; |
363 | } |
364 | |
365 | /*! |
366 | Returns true if status() == QDeclarativeComponent::Error. |
367 | */ |
368 | bool QDeclarativeComponent::isError() const |
369 | { |
370 | return status() == Error; |
371 | } |
372 | |
373 | /*! |
374 | Returns true if status() == QDeclarativeComponent::Loading. |
375 | */ |
376 | bool QDeclarativeComponent::isLoading() const |
377 | { |
378 | return status() == Loading; |
379 | } |
380 | |
381 | /*! |
382 | \qmlproperty real Component::progress |
383 | The progress of loading the component, from 0.0 (nothing loaded) |
384 | to 1.0 (finished). |
385 | */ |
386 | |
387 | /*! |
388 | \property QDeclarativeComponent::progress |
389 | The progress of loading the component, from 0.0 (nothing loaded) |
390 | to 1.0 (finished). |
391 | */ |
392 | qreal QDeclarativeComponent::progress() const |
393 | { |
394 | Q_D(const QDeclarativeComponent); |
395 | return d->progress; |
396 | } |
397 | |
398 | /*! |
399 | \fn void QDeclarativeComponent::progressChanged(qreal progress) |
400 | |
401 | Emitted whenever the component's loading progress changes. \a progress will be the |
402 | current progress between 0.0 (nothing loaded) and 1.0 (finished). |
403 | */ |
404 | |
405 | /*! |
406 | \fn void QDeclarativeComponent::statusChanged(QDeclarativeComponent::Status status) |
407 | |
408 | Emitted whenever the component's status changes. \a status will be the |
409 | new status. |
410 | */ |
411 | |
412 | /*! |
413 | Create a QDeclarativeComponent with no data and give it the specified |
414 | \a engine and \a parent. Set the data with setData(). |
415 | */ |
416 | QDeclarativeComponent::QDeclarativeComponent(QDeclarativeEngine *engine, QObject *parent) |
417 | : QObject(*(new QDeclarativeComponentPrivate), parent) |
418 | { |
419 | Q_D(QDeclarativeComponent); |
420 | d->engine = engine; |
421 | } |
422 | |
423 | /*! |
424 | Create a QDeclarativeComponent from the given \a url and give it the |
425 | specified \a parent and \a engine. |
426 | |
427 | Ensure that the URL provided is full and correct, in particular, use |
428 | \l QUrl::fromLocalFile() when loading a file from the local filesystem. |
429 | |
430 | \sa loadUrl() |
431 | */ |
432 | QDeclarativeComponent::QDeclarativeComponent(QDeclarativeEngine *engine, const QUrl &url, QObject *parent) |
433 | : QObject(*(new QDeclarativeComponentPrivate), parent) |
434 | { |
435 | Q_D(QDeclarativeComponent); |
436 | d->engine = engine; |
437 | loadUrl(url); |
438 | } |
439 | |
440 | /*! |
441 | Create a QDeclarativeComponent from the given \a fileName and give it the specified |
442 | \a parent and \a engine. |
443 | |
444 | \sa loadUrl() |
445 | */ |
446 | QDeclarativeComponent::QDeclarativeComponent(QDeclarativeEngine *engine, const QString &fileName, |
447 | QObject *parent) |
448 | : QObject(*(new QDeclarativeComponentPrivate), parent) |
449 | { |
450 | Q_D(QDeclarativeComponent); |
451 | d->engine = engine; |
452 | loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName))); |
453 | } |
454 | |
455 | /*! |
456 | \internal |
457 | */ |
458 | QDeclarativeComponent::QDeclarativeComponent(QDeclarativeEngine *engine, QDeclarativeCompiledData *cc, int start, int count, QObject *parent) |
459 | : QObject(*(new QDeclarativeComponentPrivate), parent) |
460 | { |
461 | Q_D(QDeclarativeComponent); |
462 | d->engine = engine; |
463 | d->cc = cc; |
464 | cc->addref(); |
465 | d->start = start; |
466 | d->count = count; |
467 | d->url = cc->url; |
468 | d->progress = 1.0; |
469 | } |
470 | |
471 | /*! |
472 | Sets the QDeclarativeComponent to use the given QML \a data. If \a url |
473 | is provided, it is used to set the component name and to provide |
474 | a base path for items resolved by this component. |
475 | */ |
476 | void QDeclarativeComponent::setData(const QByteArray &data, const QUrl &url) |
477 | { |
478 | Q_D(QDeclarativeComponent); |
479 | |
480 | d->clear(); |
481 | |
482 | d->url = url; |
483 | |
484 | QDeclarativeTypeData *typeData = QDeclarativeEnginePrivate::get(d->engine)->typeLoader.get(data, url); |
485 | |
486 | if (typeData->isCompleteOrError()) { |
487 | d->fromTypeData(typeData); |
488 | } else { |
489 | d->typeData = typeData; |
490 | d->typeData->registerCallback(d); |
491 | } |
492 | |
493 | d->progress = 1.0; |
494 | emit statusChanged(status()); |
495 | emit progressChanged(d->progress); |
496 | } |
497 | |
498 | /*! |
499 | Returns the QDeclarativeContext the component was created in. This is only |
500 | valid for components created directly from QML. |
501 | */ |
502 | QDeclarativeContext *QDeclarativeComponent::creationContext() const |
503 | { |
504 | Q_D(const QDeclarativeComponent); |
505 | if(d->creationContext) |
506 | return d->creationContext->asQDeclarativeContext(); |
507 | |
508 | return qmlContext(this); |
509 | } |
510 | |
511 | /*! |
512 | Load the QDeclarativeComponent from the provided \a url. |
513 | |
514 | Ensure that the URL provided is full and correct, in particular, use |
515 | \l QUrl::fromLocalFile() when loading a file from the local filesystem. |
516 | */ |
517 | void QDeclarativeComponent::loadUrl(const QUrl &url) |
518 | { |
519 | Q_D(QDeclarativeComponent); |
520 | |
521 | d->clear(); |
522 | |
523 | if ((url.isRelative() && !url.isEmpty()) |
524 | || url.scheme() == QLatin1String("file" )) // Workaround QTBUG-11929 |
525 | d->url = d->engine->baseUrl().resolved(url); |
526 | else |
527 | d->url = url; |
528 | |
529 | if (url.isEmpty()) { |
530 | QDeclarativeError error; |
531 | error.setDescription(tr("Invalid empty URL" )); |
532 | d->state.errors << error; |
533 | return; |
534 | } |
535 | |
536 | QDeclarativeTypeData *data = QDeclarativeEnginePrivate::get(d->engine)->typeLoader.get(d->url); |
537 | |
538 | if (data->isCompleteOrError()) { |
539 | d->fromTypeData(data); |
540 | d->progress = 1.0; |
541 | } else { |
542 | d->typeData = data; |
543 | d->typeData->registerCallback(d); |
544 | d->progress = data->progress(); |
545 | } |
546 | |
547 | emit statusChanged(status()); |
548 | emit progressChanged(d->progress); |
549 | } |
550 | |
551 | /*! |
552 | Return the list of errors that occurred during the last compile or create |
553 | operation. An empty list is returned if isError() is not set. |
554 | */ |
555 | QList<QDeclarativeError> QDeclarativeComponent::errors() const |
556 | { |
557 | Q_D(const QDeclarativeComponent); |
558 | if (isError()) |
559 | return d->state.errors; |
560 | else |
561 | return QList<QDeclarativeError>(); |
562 | } |
563 | |
564 | /*! |
565 | \qmlmethod string Component::errorString() |
566 | |
567 | Returns a human-readable description of any errors. |
568 | |
569 | The string includes the file, location, and description of each error. |
570 | If multiple errors are present they are separated by a newline character. |
571 | |
572 | If no errors are present, an empty string is returned. |
573 | */ |
574 | |
575 | /*! |
576 | \internal |
577 | errorString is only meant as a way to get the errors in script |
578 | */ |
579 | QString QDeclarativeComponent::errorString() const |
580 | { |
581 | Q_D(const QDeclarativeComponent); |
582 | QString ret; |
583 | if(!isError()) |
584 | return ret; |
585 | foreach(const QDeclarativeError &e, d->state.errors) { |
586 | ret += e.url().toString() + QLatin1Char(':') + |
587 | QString::number(e.line()) + QLatin1Char(' ') + |
588 | e.description() + QLatin1Char('\n'); |
589 | } |
590 | return ret; |
591 | } |
592 | |
593 | /*! |
594 | \qmlproperty url Component::url |
595 | The component URL. This is the URL that was used to construct the component. |
596 | */ |
597 | |
598 | /*! |
599 | \property QDeclarativeComponent::url |
600 | The component URL. This is the URL passed to either the constructor, |
601 | or the loadUrl() or setData() methods. |
602 | */ |
603 | QUrl QDeclarativeComponent::url() const |
604 | { |
605 | Q_D(const QDeclarativeComponent); |
606 | return d->url; |
607 | } |
608 | |
609 | /*! |
610 | \internal |
611 | */ |
612 | QDeclarativeComponent::QDeclarativeComponent(QDeclarativeComponentPrivate &dd, QObject *parent) |
613 | : QObject(dd, parent) |
614 | { |
615 | } |
616 | |
617 | /*! |
618 | \qmlmethod object Component::createObject(Item parent, object properties) |
619 | |
620 | Creates and returns an object instance of this component that will have |
621 | the given \a parent and \a properties. The \a properties argument is optional. |
622 | Returns null if object creation fails. |
623 | |
624 | The object will be created in the same context as the one in which the component |
625 | was created. This function will always return null when called on components |
626 | which were not created in QML. |
627 | |
628 | If you wish to create an object without setting a parent, specify \c null for |
629 | the \a parent value. Note that if the returned object is to be displayed, you |
630 | must provide a valid \a parent value or set the returned object's \l{Item::parent}{parent} |
631 | property, or else the object will not be visible. |
632 | |
633 | If a \a parent is not provided to createObject(), a reference to the returned object must be held so that |
634 | it is not destroyed by the garbage collector. This is true regardless of whether \l{Item::parent} is set afterwards, |
635 | since setting the Item parent does not change object ownership; only the graphical parent is changed. |
636 | |
637 | As of QtQuick 1.1, this method accepts an optional \a properties argument that specifies a |
638 | map of initial property values for the created object. These values are applied before object |
639 | creation is finalized. (This is more efficient than setting property values after object creation, |
640 | particularly where large sets of property values are defined, and also allows property bindings |
641 | to be set up before the object is created.) |
642 | |
643 | The \a properties argument is specified as a map of property-value items. For example, the code |
644 | below creates an object with initial \c x and \c y values of 100 and 200, respectively: |
645 | |
646 | \js |
647 | var component = Qt.createComponent("Button.qml"); |
648 | if (component.status == Component.Ready) |
649 | component.createObject(parent, {"x": 100, "y": 100}); |
650 | \endjs |
651 | |
652 | Dynamically created instances can be deleted with the \c destroy() method. |
653 | See \l {Dynamic Object Management in QML} for more information. |
654 | */ |
655 | |
656 | /*! |
657 | \internal |
658 | A version of create which returns a scriptObject, for use in script. |
659 | This function will only work on components created in QML. |
660 | |
661 | Sets graphics object parent because forgetting to do this is a frequent |
662 | and serious problem. |
663 | */ |
664 | QScriptValue QDeclarativeComponent::createObject(QObject* parent) |
665 | { |
666 | Q_D(QDeclarativeComponent); |
667 | return d->createObject(parent, QScriptValue(QScriptValue::NullValue)); |
668 | } |
669 | |
670 | /*! |
671 | \internal |
672 | Overloadable method allows properties to be set during creation |
673 | */ |
674 | QScriptValue QDeclarativeComponent::createObject(QObject* parent, const QScriptValue& valuemap) |
675 | { |
676 | Q_D(QDeclarativeComponent); |
677 | |
678 | if (!valuemap.isObject() || valuemap.isArray()) { |
679 | qmlInfo(this) << tr("createObject: value is not an object" ); |
680 | return QScriptValue(QScriptValue::NullValue); |
681 | } |
682 | return d->createObject(parent, valuemap); |
683 | } |
684 | |
685 | QScriptValue QDeclarativeComponentPrivate::createObject(QObject *publicParent, const QScriptValue valuemap) |
686 | { |
687 | Q_Q(QDeclarativeComponent); |
688 | QDeclarativeContext* ctxt = q->creationContext(); |
689 | if(!ctxt && engine) |
690 | ctxt = engine->rootContext(); |
691 | if (!ctxt) |
692 | return QScriptValue(QScriptValue::NullValue); |
693 | QObject* ret = q->beginCreate(ctxt); |
694 | if (!ret) { |
695 | q->completeCreate(); |
696 | return QScriptValue(QScriptValue::NullValue); |
697 | } |
698 | |
699 | if (publicParent) { |
700 | ret->setParent(publicParent); |
701 | QList<QDeclarativePrivate::AutoParentFunction> functions = QDeclarativeMetaType::parentFunctions(); |
702 | |
703 | bool needParent = false; |
704 | |
705 | for (int ii = 0; ii < functions.count(); ++ii) { |
706 | QDeclarativePrivate::AutoParentResult res = functions.at(ii)(ret, publicParent); |
707 | if (res == QDeclarativePrivate::Parented) { |
708 | needParent = false; |
709 | break; |
710 | } else if (res == QDeclarativePrivate::IncompatibleParent) { |
711 | needParent = true; |
712 | } |
713 | } |
714 | |
715 | if (needParent) |
716 | qWarning("QDeclarativeComponent: Created graphical object was not placed in the graphics scene." ); |
717 | } |
718 | |
719 | QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(engine); |
720 | QDeclarativeData::get(ret, true)->setImplicitDestructible(); |
721 | QScriptValue newObject = priv->objectClass->newQObject(ret, QMetaType::QObjectStar); |
722 | |
723 | if (valuemap.isObject() && !valuemap.isArray()) { |
724 | //Iterate through and assign properties |
725 | QScriptValueIterator it(valuemap); |
726 | while (it.hasNext()) { |
727 | it.next(); |
728 | QScriptValue prop = newObject; |
729 | QString propName = it.name(); |
730 | int index = propName.indexOf(QLatin1Char('.')); |
731 | if (index > 0) { |
732 | QString subProp = propName; |
733 | int lastIndex = 0; |
734 | while (index > 0) { |
735 | subProp = propName.mid(lastIndex, index - lastIndex); |
736 | prop = prop.property(subProp); |
737 | lastIndex = index + 1; |
738 | index = propName.indexOf(QLatin1Char('.'), index + 1); |
739 | } |
740 | prop.setProperty(propName.mid(propName.lastIndexOf(QLatin1Char('.')) + 1), it.value()); |
741 | } else { |
742 | newObject.setProperty(propName, it.value()); |
743 | } |
744 | } |
745 | } |
746 | |
747 | q->completeCreate(); |
748 | |
749 | return newObject; |
750 | } |
751 | |
752 | /*! |
753 | Create an object instance from this component. Returns 0 if creation |
754 | failed. \a context specifies the context within which to create the object |
755 | instance. |
756 | |
757 | If \a context is 0 (the default), it will create the instance in the |
758 | engine' s \l {QDeclarativeEngine::rootContext()}{root context}. |
759 | */ |
760 | QObject *QDeclarativeComponent::create(QDeclarativeContext *context) |
761 | { |
762 | Q_D(QDeclarativeComponent); |
763 | |
764 | if (!context) |
765 | context = d->engine->rootContext(); |
766 | |
767 | QObject *rv = beginCreate(context); |
768 | completeCreate(); |
769 | return rv; |
770 | } |
771 | |
772 | /*! |
773 | This method provides more advanced control over component instance creation. |
774 | In general, programmers should use QDeclarativeComponent::create() to create a |
775 | component. |
776 | |
777 | Create an object instance from this component. Returns 0 if creation |
778 | failed. \a context specifies the context within which to create the object |
779 | instance. |
780 | |
781 | When QDeclarativeComponent constructs an instance, it occurs in three steps: |
782 | \list 1 |
783 | \i The object hierarchy is created, and constant values are assigned. |
784 | \i Property bindings are evaluated for the the first time. |
785 | \i If applicable, QDeclarativeParserStatus::componentComplete() is called on objects. |
786 | \endlist |
787 | QDeclarativeComponent::beginCreate() differs from QDeclarativeComponent::create() in that it |
788 | only performs step 1. QDeclarativeComponent::completeCreate() must be called to |
789 | complete steps 2 and 3. |
790 | |
791 | This breaking point is sometimes useful when using attached properties to |
792 | communicate information to an instantiated component, as it allows their |
793 | initial values to be configured before property bindings take effect. |
794 | */ |
795 | QObject *QDeclarativeComponent::beginCreate(QDeclarativeContext *context) |
796 | { |
797 | Q_D(QDeclarativeComponent); |
798 | QObject *rv = d->beginCreate(context?QDeclarativeContextData::get(context):0, QBitField()); |
799 | if (rv) { |
800 | QDeclarativeData *ddata = QDeclarativeData::get(rv); |
801 | Q_ASSERT(ddata); |
802 | ddata->indestructible = true; |
803 | } |
804 | return rv; |
805 | } |
806 | |
807 | QObject * |
808 | QDeclarativeComponentPrivate::beginCreate(QDeclarativeContextData *context, const QBitField &bindings) |
809 | { |
810 | Q_Q(QDeclarativeComponent); |
811 | if (!context) { |
812 | qWarning("QDeclarativeComponent: Cannot create a component in a null context" ); |
813 | return 0; |
814 | } |
815 | |
816 | if (!context->isValid()) { |
817 | qWarning("QDeclarativeComponent: Cannot create a component in an invalid context" ); |
818 | return 0; |
819 | } |
820 | |
821 | if (context->engine != engine) { |
822 | qWarning("QDeclarativeComponent: Must create component in context from the same QDeclarativeEngine" ); |
823 | return 0; |
824 | } |
825 | |
826 | if (state.completePending) { |
827 | qWarning("QDeclarativeComponent: Cannot create new component instance before completing the previous" ); |
828 | return 0; |
829 | } |
830 | |
831 | if (!q->isReady()) { |
832 | qWarning("QDeclarativeComponent: Component is not ready" ); |
833 | return 0; |
834 | } |
835 | |
836 | return begin(context, creationContext, cc, start, count, &state, 0, bindings); |
837 | } |
838 | |
839 | QObject * QDeclarativeComponentPrivate::begin(QDeclarativeContextData *parentContext, |
840 | QDeclarativeContextData *componentCreationContext, |
841 | QDeclarativeCompiledData *component, int start, int count, |
842 | ConstructionState *state, QList<QDeclarativeError> *errors, |
843 | const QBitField &bindings) |
844 | { |
845 | QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(parentContext->engine); |
846 | bool isRoot = !enginePriv->inBeginCreate; |
847 | |
848 | Q_ASSERT(!isRoot || state); // Either this isn't a root component, or a state data must be provided |
849 | Q_ASSERT((state != 0) ^ (errors != 0)); // One of state or errors (but not both) must be provided |
850 | |
851 | if (isRoot) { |
852 | QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Creating); |
853 | QDeclarativeDebugTrace::rangeData(QDeclarativeDebugTrace::Creating, component->url); |
854 | } |
855 | |
856 | QDeclarativeContextData *ctxt = new QDeclarativeContextData; |
857 | ctxt->isInternal = true; |
858 | ctxt->url = component->url; |
859 | ctxt->imports = component->importCache; |
860 | |
861 | // Nested global imports |
862 | if (componentCreationContext && start != -1) |
863 | ctxt->importedScripts = componentCreationContext->importedScripts; |
864 | |
865 | component->importCache->addref(); |
866 | ctxt->setParent(parentContext); |
867 | |
868 | enginePriv->inBeginCreate = true; |
869 | |
870 | QDeclarativeVME vme; |
871 | QObject *rv = vme.run(ctxt, component, start, count, bindings); |
872 | |
873 | if (vme.isError()) { |
874 | if(errors) *errors = vme.errors(); |
875 | else state->errors = vme.errors(); |
876 | } |
877 | |
878 | if (isRoot) { |
879 | enginePriv->inBeginCreate = false; |
880 | |
881 | state->bindValues = enginePriv->bindValues; |
882 | state->parserStatus = enginePriv->parserStatus; |
883 | state->finalizedParserStatus = enginePriv->finalizedParserStatus; |
884 | state->componentAttached = enginePriv->componentAttached; |
885 | if (state->componentAttached) |
886 | state->componentAttached->prev = &state->componentAttached; |
887 | |
888 | enginePriv->componentAttached = 0; |
889 | enginePriv->bindValues.clear(); |
890 | enginePriv->parserStatus.clear(); |
891 | enginePriv->finalizedParserStatus.clear(); |
892 | state->completePending = true; |
893 | enginePriv->inProgressCreations++; |
894 | } |
895 | |
896 | if (enginePriv->isDebugging && rv) { |
897 | if (!parentContext->isInternal) |
898 | parentContext->asQDeclarativeContextPrivate()->instances.append(rv); |
899 | QDeclarativeEngineDebugService::instance()->objectCreated(parentContext->engine, rv); |
900 | } |
901 | |
902 | return rv; |
903 | } |
904 | |
905 | void QDeclarativeComponentPrivate::beginDeferred(QDeclarativeEnginePrivate *enginePriv, |
906 | QObject *object, ConstructionState *state) |
907 | { |
908 | bool isRoot = !enginePriv->inBeginCreate; |
909 | enginePriv->inBeginCreate = true; |
910 | |
911 | QDeclarativeVME vme; |
912 | vme.runDeferred(object); |
913 | |
914 | if (vme.isError()) |
915 | state->errors = vme.errors(); |
916 | |
917 | if (isRoot) { |
918 | enginePriv->inBeginCreate = false; |
919 | |
920 | state->bindValues = enginePriv->bindValues; |
921 | state->parserStatus = enginePriv->parserStatus; |
922 | state->finalizedParserStatus = enginePriv->finalizedParserStatus; |
923 | state->componentAttached = enginePriv->componentAttached; |
924 | if (state->componentAttached) |
925 | state->componentAttached->prev = &state->componentAttached; |
926 | |
927 | enginePriv->componentAttached = 0; |
928 | enginePriv->bindValues.clear(); |
929 | enginePriv->parserStatus.clear(); |
930 | enginePriv->finalizedParserStatus.clear(); |
931 | state->completePending = true; |
932 | enginePriv->inProgressCreations++; |
933 | } |
934 | } |
935 | |
936 | void QDeclarativeComponentPrivate::complete(QDeclarativeEnginePrivate *enginePriv, ConstructionState *state) |
937 | { |
938 | if (state->completePending) { |
939 | QT_TRY { |
940 | for (int ii = 0; ii < state->bindValues.count(); ++ii) { |
941 | QDeclarativeEnginePrivate::SimpleList<QDeclarativeAbstractBinding> bv = |
942 | state->bindValues.at(ii); |
943 | for (int jj = 0; jj < bv.count; ++jj) { |
944 | if(bv.at(jj)) { |
945 | // XXX akennedy |
946 | bv.at(jj)->m_mePtr = 0; |
947 | bv.at(jj)->setEnabled(true, QDeclarativePropertyPrivate::BypassInterceptor | |
948 | QDeclarativePropertyPrivate::DontRemoveBinding); |
949 | } |
950 | } |
951 | QDeclarativeEnginePrivate::clear(bv); |
952 | } |
953 | |
954 | for (int ii = 0; ii < state->parserStatus.count(); ++ii) { |
955 | QDeclarativeEnginePrivate::SimpleList<QDeclarativeParserStatus> ps = |
956 | state->parserStatus.at(ii); |
957 | |
958 | for (int jj = ps.count - 1; jj >= 0; --jj) { |
959 | QDeclarativeParserStatus *status = ps.at(jj); |
960 | if (status && status->d) { |
961 | status->d = 0; |
962 | status->componentComplete(); |
963 | } |
964 | } |
965 | QDeclarativeEnginePrivate::clear(ps); |
966 | } |
967 | |
968 | for (int ii = 0; ii < state->finalizedParserStatus.count(); ++ii) { |
969 | QPair<QDeclarativeGuard<QObject>, int> status = state->finalizedParserStatus.at(ii); |
970 | QObject *obj = status.first; |
971 | if (obj) { |
972 | void *args[] = { 0 }; |
973 | QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, |
974 | status.second, args); |
975 | } |
976 | } |
977 | |
978 | //componentComplete() can register additional finalization objects |
979 | //that are then never handled. Handle them manually here. |
980 | if (1 == enginePriv->inProgressCreations) { |
981 | for (int ii = 0; ii < enginePriv->finalizedParserStatus.count(); ++ii) { |
982 | QPair<QDeclarativeGuard<QObject>, int> status = enginePriv->finalizedParserStatus.at(ii); |
983 | QObject *obj = status.first; |
984 | if (obj) { |
985 | void *args[] = { 0 }; |
986 | QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, |
987 | status.second, args); |
988 | } |
989 | } |
990 | enginePriv->finalizedParserStatus.clear(); |
991 | } |
992 | |
993 | while (state->componentAttached) { |
994 | QDeclarativeComponentAttached *a = state->componentAttached; |
995 | a->rem(); |
996 | QDeclarativeData *d = QDeclarativeData::get(a->parent()); |
997 | Q_ASSERT(d); |
998 | Q_ASSERT(d->context); |
999 | a->add(&d->context->componentAttached); |
1000 | emit a->completed(); |
1001 | } |
1002 | } QT_CATCH(const std::exception&) { |
1003 | state->bindValues.clear(); |
1004 | state->parserStatus.clear(); |
1005 | state->finalizedParserStatus.clear(); |
1006 | state->completePending = false; |
1007 | enginePriv->inProgressCreations--; |
1008 | QT_RETHROW; |
1009 | } |
1010 | |
1011 | state->bindValues.clear(); |
1012 | state->parserStatus.clear(); |
1013 | state->finalizedParserStatus.clear(); |
1014 | state->completePending = false; |
1015 | |
1016 | enginePriv->inProgressCreations--; |
1017 | if (0 == enginePriv->inProgressCreations) { |
1018 | while (enginePriv->erroredBindings) { |
1019 | enginePriv->warning(enginePriv->erroredBindings->error); |
1020 | enginePriv->erroredBindings->removeError(); |
1021 | } |
1022 | } |
1023 | } |
1024 | } |
1025 | |
1026 | /*! |
1027 | This method provides more advanced control over component instance creation. |
1028 | In general, programmers should use QDeclarativeComponent::create() to create a |
1029 | component. |
1030 | |
1031 | Complete a component creation begin with QDeclarativeComponent::beginCreate(). |
1032 | */ |
1033 | void QDeclarativeComponent::completeCreate() |
1034 | { |
1035 | Q_D(QDeclarativeComponent); |
1036 | d->completeCreate(); |
1037 | } |
1038 | |
1039 | void QDeclarativeComponentPrivate::completeCreate() |
1040 | { |
1041 | if (state.completePending) { |
1042 | QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); |
1043 | complete(ep, &state); |
1044 | |
1045 | QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Creating); |
1046 | } |
1047 | } |
1048 | |
1049 | QDeclarativeComponentAttached::QDeclarativeComponentAttached(QObject *parent) |
1050 | : QObject(parent), prev(0), next(0) |
1051 | { |
1052 | } |
1053 | |
1054 | QDeclarativeComponentAttached::~QDeclarativeComponentAttached() |
1055 | { |
1056 | if (prev) *prev = next; |
1057 | if (next) next->prev = prev; |
1058 | prev = 0; |
1059 | next = 0; |
1060 | } |
1061 | |
1062 | /*! |
1063 | \internal |
1064 | */ |
1065 | QDeclarativeComponentAttached *QDeclarativeComponent::qmlAttachedProperties(QObject *obj) |
1066 | { |
1067 | QDeclarativeComponentAttached *a = new QDeclarativeComponentAttached(obj); |
1068 | |
1069 | QDeclarativeEngine *engine = qmlEngine(obj); |
1070 | if (!engine) |
1071 | return a; |
1072 | |
1073 | if (QDeclarativeEnginePrivate::get(engine)->inBeginCreate) { |
1074 | QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine); |
1075 | a->add(&p->componentAttached); |
1076 | } else { |
1077 | QDeclarativeData *d = QDeclarativeData::get(obj); |
1078 | Q_ASSERT(d); |
1079 | Q_ASSERT(d->context); |
1080 | a->add(&d->context->componentAttached); |
1081 | } |
1082 | |
1083 | return a; |
1084 | } |
1085 | |
1086 | QT_END_NAMESPACE |
1087 | |