source: trunk/src/declarative/graphicsitems/qdeclarativeloader.cpp@ 986

Last change on this file since 986 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 17.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtDeclarative module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "private/qdeclarativeloader_p_p.h"
43
44#include <qdeclarativeinfo.h>
45#include <qdeclarativeengine_p.h>
46#include <qdeclarativeglobal_p.h>
47
48QT_BEGIN_NAMESPACE
49
50QDeclarativeLoaderPrivate::QDeclarativeLoaderPrivate()
51 : item(0), component(0), ownComponent(false)
52{
53}
54
55QDeclarativeLoaderPrivate::~QDeclarativeLoaderPrivate()
56{
57}
58
59void QDeclarativeLoaderPrivate::itemGeometryChanged(QDeclarativeItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry)
60{
61 if (resizeItem == item)
62 _q_updateSize(false);
63 QDeclarativeItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry);
64}
65
66void QDeclarativeLoaderPrivate::clear()
67{
68 if (ownComponent) {
69 component->deleteLater();
70 component = 0;
71 ownComponent = false;
72 }
73 source = QUrl();
74
75 if (item) {
76 if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(item)) {
77 QDeclarativeItemPrivate *p =
78 static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(qmlItem));
79 p->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
80 }
81
82 // We can't delete immediately because our item may have triggered
83 // the Loader to load a different item.
84 if (item->scene()) {
85 item->scene()->removeItem(item);
86 } else {
87 item->setParentItem(0);
88 item->setVisible(false);
89 }
90 item->deleteLater();
91 item = 0;
92 }
93}
94
95void QDeclarativeLoaderPrivate::initResize()
96{
97 Q_Q(QDeclarativeLoader);
98 if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(item)) {
99 QDeclarativeItemPrivate *p =
100 static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(qmlItem));
101 p->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
102 } else if (item && item->isWidget()) {
103 QGraphicsWidget *widget = static_cast<QGraphicsWidget*>(item);
104 widget->installEventFilter(q);
105 }
106 _q_updateSize();
107}
108
109/*!
110 \qmlclass Loader QDeclarativeLoader
111 \ingroup qml-utility-elements
112 \since 4.7
113 \inherits Item
114
115 \brief The Loader item allows dynamically loading an Item-based
116 subtree from a URL or Component.
117
118 Loader is used to dynamically load visual QML components. It can load a
119 QML file (using the \l source property) or a \l Component object (using
120 the \l sourceComponent property). It is useful for delaying the creation
121 of a component until it is required: for example, when a component should
122 be created on demand, or when a component should not be created
123 unnecessarily for performance reasons.
124
125 Here is a Loader that loads "Page1.qml" as a component when the
126 \l MouseArea is clicked:
127
128 \snippet doc/src/snippets/declarative/loader/simple.qml 0
129
130 The loaded item can be accessed using the \l item property.
131
132 If the \l source or \l sourceComponent changes, any previously instantiated
133 items are destroyed. Setting \l source to an empty string or setting
134 \l sourceComponent to \c undefined destroys the currently loaded item,
135 freeing resources and leaving the Loader empty.
136
137 \section2 Loader sizing behavior
138
139 Loader is like any other visual item and must be positioned and sized
140 accordingly to become visible.
141
142 \list
143 \o If an explicit size is not specified for the Loader, the Loader
144 is automatically resized to the size of the loaded item once the
145 component is loaded.
146 \o If the size of the Loader is specified explicitly by setting
147 the width, height or by anchoring, the loaded item will be resized
148 to the size of the Loader.
149 \endlist
150
151 In both scenarios the size of the item and the Loader are identical.
152 This ensures that anchoring to the Loader is equivalent to anchoring
153 to the loaded item.
154
155 \table
156 \row
157 \o sizeloader.qml
158 \o sizeitem.qml
159 \row
160 \o \snippet doc/src/snippets/declarative/loader/sizeloader.qml 0
161 \o \snippet doc/src/snippets/declarative/loader/sizeitem.qml 0
162 \row
163 \o The red rectangle will be sized to the size of the root item.
164 \o The red rectangle will be 50x50, centered in the root item.
165 \endtable
166
167
168 \section2 Receiving signals from loaded items
169
170 Any signals emitted from the loaded item can be received using the
171 \l Connections element. For example, the following \c application.qml
172 loads \c MyItem.qml, and is able to receive the \c message signal from
173 the loaded item through a \l Connections object:
174
175 \table
176 \row
177 \o application.qml
178 \o MyItem.qml
179 \row
180 \o \snippet doc/src/snippets/declarative/loader/connections.qml 0
181 \o \snippet doc/src/snippets/declarative/loader/MyItem.qml 0
182 \endtable
183
184 Alternatively, since \c MyItem.qml is loaded within the scope of the
185 Loader, it could also directly call any function defined in the Loader or
186 its parent \l Item.
187
188
189 \section2 Focus and key events
190
191 Loader is a focus scope. Its \l {Item::}{focus} property must be set to
192 \c true for any of its children to get the \e {active focus}. (See
193 \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page}
194 for more details.) Any key events received in the loaded item should likely
195 also be \l {KeyEvent::}{accepted} so they are not propagated to the Loader.
196
197 For example, the following \c application.qml loads \c KeyReader.qml when
198 the \l MouseArea is clicked. Notice the \l {Item::}{focus} property is
199 set to \c true for the Loader as well as the \l Item in the dynamically
200 loaded object:
201
202 \table
203 \row
204 \o application.qml
205 \o KeyReader.qml
206 \row
207 \o \snippet doc/src/snippets/declarative/loader/focus.qml 0
208 \o \snippet doc/src/snippets/declarative/loader/KeyReader.qml 0
209 \endtable
210
211 Once \c KeyReader.qml is loaded, it accepts key events and sets
212 \c event.accepted to \c true so that the event is not propagated to the
213 parent \l Rectangle.
214
215 \sa {dynamic-object-creation}{Dynamic Object Creation}
216*/
217
218QDeclarativeLoader::QDeclarativeLoader(QDeclarativeItem *parent)
219 : QDeclarativeItem(*(new QDeclarativeLoaderPrivate), parent)
220{
221 Q_D(QDeclarativeLoader);
222 d->flags |= QGraphicsItem::ItemIsFocusScope;
223}
224
225QDeclarativeLoader::~QDeclarativeLoader()
226{
227 Q_D(QDeclarativeLoader);
228 if (d->item) {
229 if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(d->item)) {
230 QDeclarativeItemPrivate *p =
231 static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(qmlItem));
232 p->removeItemChangeListener(d, QDeclarativeItemPrivate::Geometry);
233 }
234 }
235}
236
237/*!
238 \qmlproperty url Loader::source
239 This property holds the URL of the QML component to instantiate.
240
241 Note the QML component must be an \l{Item}-based component. The loader
242 cannot load non-visual components.
243
244 To unload the currently loaded item, set this property to an empty string,
245 or set \l sourceComponent to \c undefined. Setting \c source to a
246 new URL will also cause the item created by the previous URL to be unloaded.
247
248 \sa sourceComponent, status, progress
249*/
250QUrl QDeclarativeLoader::source() const
251{
252 Q_D(const QDeclarativeLoader);
253 return d->source;
254}
255
256void QDeclarativeLoader::setSource(const QUrl &url)
257{
258 Q_D(QDeclarativeLoader);
259 if (d->source == url)
260 return;
261
262 d->clear();
263
264 d->source = url;
265 if (d->source.isEmpty()) {
266 emit sourceChanged();
267 emit statusChanged();
268 emit progressChanged();
269 emit itemChanged();
270 return;
271 }
272
273 d->component = new QDeclarativeComponent(qmlEngine(this), d->source, this);
274 d->ownComponent = true;
275 if (!d->component->isLoading()) {
276 d->_q_sourceLoaded();
277 } else {
278 connect(d->component, SIGNAL(statusChanged(QDeclarativeComponent::Status)),
279 this, SLOT(_q_sourceLoaded()));
280 connect(d->component, SIGNAL(progressChanged(qreal)),
281 this, SIGNAL(progressChanged()));
282 emit statusChanged();
283 emit progressChanged();
284 emit sourceChanged();
285 emit itemChanged();
286 }
287}
288
289/*!
290 \qmlproperty Component Loader::sourceComponent
291 This property holds the \l{Component} to instantiate.
292
293 \qml
294 Item {
295 Component {
296 id: redSquare
297 Rectangle { color: "red"; width: 10; height: 10 }
298 }
299
300 Loader { sourceComponent: redSquare }
301 Loader { sourceComponent: redSquare; x: 10 }
302 }
303 \endqml
304
305 To unload the currently loaded item, set this property to an empty string
306 or \c undefined.
307
308 \sa source, progress
309*/
310
311QDeclarativeComponent *QDeclarativeLoader::sourceComponent() const
312{
313 Q_D(const QDeclarativeLoader);
314 return d->component;
315}
316
317void QDeclarativeLoader::setSourceComponent(QDeclarativeComponent *comp)
318{
319 Q_D(QDeclarativeLoader);
320 if (comp == d->component)
321 return;
322
323 d->clear();
324
325 d->component = comp;
326 d->ownComponent = false;
327 if (!d->component) {
328 emit sourceChanged();
329 emit statusChanged();
330 emit progressChanged();
331 emit itemChanged();
332 return;
333 }
334
335 if (!d->component->isLoading()) {
336 d->_q_sourceLoaded();
337 } else {
338 connect(d->component, SIGNAL(statusChanged(QDeclarativeComponent::Status)),
339 this, SLOT(_q_sourceLoaded()));
340 connect(d->component, SIGNAL(progressChanged(qreal)),
341 this, SIGNAL(progressChanged()));
342 emit progressChanged();
343 emit sourceChanged();
344 emit statusChanged();
345 emit itemChanged();
346 }
347}
348
349void QDeclarativeLoader::resetSourceComponent()
350{
351 setSourceComponent(0);
352}
353
354void QDeclarativeLoaderPrivate::_q_sourceLoaded()
355{
356 Q_Q(QDeclarativeLoader);
357
358 if (component) {
359 if (!component->errors().isEmpty()) {
360 QDeclarativeEnginePrivate::warning(qmlEngine(q), component->errors());
361 emit q->sourceChanged();
362 emit q->statusChanged();
363 emit q->progressChanged();
364 return;
365 }
366
367 QDeclarativeContext *creationContext = component->creationContext();
368 if (!creationContext) creationContext = qmlContext(q);
369 QDeclarativeContext *ctxt = new QDeclarativeContext(creationContext);
370 ctxt->setContextObject(q);
371
372 QDeclarativeGuard<QDeclarativeComponent> c = component;
373 QObject *obj = component->beginCreate(ctxt);
374 if (component != c) {
375 // component->create could trigger a change in source that causes
376 // component to be set to something else. In that case we just
377 // need to cleanup.
378 if (c)
379 c->completeCreate();
380 delete obj;
381 delete ctxt;
382 return;
383 }
384 if (obj) {
385 item = qobject_cast<QGraphicsObject *>(obj);
386 if (item) {
387 QDeclarative_setParent_noEvent(ctxt, obj);
388 QDeclarative_setParent_noEvent(item, q);
389 item->setParentItem(q);
390// item->setFocus(true);
391 initResize();
392 } else {
393 qmlInfo(q) << QDeclarativeLoader::tr("Loader does not support loading non-visual elements.");
394 delete obj;
395 delete ctxt;
396 }
397 } else {
398 if (!component->errors().isEmpty())
399 QDeclarativeEnginePrivate::warning(qmlEngine(q), component->errors());
400 delete obj;
401 delete ctxt;
402 source = QUrl();
403 }
404 component->completeCreate();
405 emit q->sourceChanged();
406 emit q->statusChanged();
407 emit q->progressChanged();
408 emit q->itemChanged();
409 emit q->loaded();
410 }
411}
412
413/*!
414 \qmlproperty enumeration Loader::status
415
416 This property holds the status of QML loading. It can be one of:
417 \list
418 \o Loader.Null - no QML source has been set
419 \o Loader.Ready - the QML source has been loaded
420 \o Loader.Loading - the QML source is currently being loaded
421 \o Loader.Error - an error occurred while loading the QML source
422 \endlist
423
424 Use this status to provide an update or respond to the status change in some way.
425 For example, you could:
426
427 \list
428 \o Trigger a state change:
429 \qml
430 State { name: 'loaded'; when: loader.status == Loader.Ready }
431 \endqml
432
433 \o Implement an \c onStatusChanged signal handler:
434 \qml
435 Loader {
436 id: loader
437 onStatusChanged: if (loader.status == Loader.Ready) console.log('Loaded')
438 }
439 \endqml
440
441 \o Bind to the status value:
442 \qml
443 Text { text: loader.status == Loader.Ready ? 'Loaded' : 'Not loaded' }
444 \endqml
445 \endlist
446
447 Note that if the source is a local file, the status will initially be Ready (or Error). While
448 there will be no onStatusChanged signal in that case, the onLoaded will still be invoked.
449
450 \sa progress
451*/
452
453QDeclarativeLoader::Status QDeclarativeLoader::status() const
454{
455 Q_D(const QDeclarativeLoader);
456
457 if (d->component)
458 return static_cast<QDeclarativeLoader::Status>(d->component->status());
459
460 if (d->item)
461 return Ready;
462
463 return d->source.isEmpty() ? Null : Error;
464}
465
466void QDeclarativeLoader::componentComplete()
467{
468 QDeclarativeItem::componentComplete();
469 if (status() == Ready)
470 emit loaded();
471}
472
473
474/*!
475 \qmlsignal Loader::onLoaded()
476
477 This handler is called when the \l status becomes \c Loader.Ready, or on successful
478 initial load.
479*/
480
481
482/*!
483\qmlproperty real Loader::progress
484
485This property holds the progress of loading QML data from the network, from
4860.0 (nothing loaded) to 1.0 (finished). Most QML files are quite small, so
487this value will rapidly change from 0 to 1.
488
489\sa status
490*/
491qreal QDeclarativeLoader::progress() const
492{
493 Q_D(const QDeclarativeLoader);
494
495 if (d->item)
496 return 1.0;
497
498 if (d->component)
499 return d->component->progress();
500
501 return 0.0;
502}
503
504void QDeclarativeLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
505{
506 Q_Q(QDeclarativeLoader);
507 if (!item)
508 return;
509 if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(item)) {
510 q->setImplicitWidth(qmlItem->width());
511 if (loaderGeometryChanged && q->widthValid())
512 qmlItem->setWidth(q->width());
513 q->setImplicitHeight(qmlItem->height());
514 if (loaderGeometryChanged && q->heightValid())
515 qmlItem->setHeight(q->height());
516 } else if (item && item->isWidget()) {
517 QGraphicsWidget *widget = static_cast<QGraphicsWidget*>(item);
518 QSizeF widgetSize = widget->size();
519 q->setImplicitWidth(widgetSize.width());
520 if (loaderGeometryChanged && q->widthValid())
521 widgetSize.setWidth(q->width());
522 q->setImplicitHeight(widgetSize.height());
523 if (loaderGeometryChanged && q->heightValid())
524 widgetSize.setHeight(q->height());
525 if (widget->size() != widgetSize)
526 widget->resize(widgetSize);
527 }
528}
529
530/*!
531 \qmlproperty Item Loader::item
532 This property holds the top-level item that is currently loaded.
533*/
534QGraphicsObject *QDeclarativeLoader::item() const
535{
536 Q_D(const QDeclarativeLoader);
537 return d->item;
538}
539
540void QDeclarativeLoader::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
541{
542 Q_D(QDeclarativeLoader);
543 if (newGeometry != oldGeometry) {
544 d->_q_updateSize();
545 }
546 QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
547}
548
549QVariant QDeclarativeLoader::itemChange(GraphicsItemChange change, const QVariant &value)
550{
551 Q_D(QDeclarativeLoader);
552 if (change == ItemSceneHasChanged) {
553 if (d->item && d->item->isWidget()) {
554 d->item->removeEventFilter(this);
555 d->item->installEventFilter(this);
556 }
557 }
558 return QDeclarativeItem::itemChange(change, value);
559}
560
561bool QDeclarativeLoader::eventFilter(QObject *watched, QEvent *e)
562{
563 Q_D(QDeclarativeLoader);
564 if (watched == d->item && e->type() == QEvent::GraphicsSceneResize) {
565 if (d->item && d->item->isWidget())
566 d->_q_updateSize(false);
567 }
568 return QDeclarativeItem::eventFilter(watched, e);
569}
570
571#include <moc_qdeclarativeloader_p.cpp>
572
573QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.