1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "QtQuick1/private/qdeclarativeloader_p_p.h"
44 #include <QtDeclarative/qdeclarativeinfo.h>
45 #include <QtDeclarative/private/qdeclarativeengine_p.h>
46 #include <QtDeclarative/private/qdeclarativeglobal_p.h>
52 QDeclarative1LoaderPrivate::QDeclarative1LoaderPrivate()
53 : item(0), component(0), ownComponent(false), updatingSize(false),
54 itemWidthValid(false), itemHeightValid(false)
58 QDeclarative1LoaderPrivate::~QDeclarative1LoaderPrivate()
62 void QDeclarative1LoaderPrivate::itemGeometryChanged(QDeclarativeItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry)
64 if (resizeItem == item) {
65 if (!updatingSize && newGeometry.width() != oldGeometry.width())
66 itemWidthValid = true;
67 if (!updatingSize && newGeometry.height() != oldGeometry.height())
68 itemHeightValid = true;
71 QDeclarativeItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry);
74 void QDeclarative1LoaderPrivate::clear()
77 component->deleteLater();
84 if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(item)) {
85 QDeclarativeItemPrivate *p =
86 static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(qmlItem));
87 p->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
90 // We can't delete immediately because our item may have triggered
91 // the Loader to load a different item.
93 item->scene()->removeItem(item);
95 item->setParentItem(0);
96 item->setVisible(false);
103 void QDeclarative1LoaderPrivate::initResize()
105 Q_Q(QDeclarative1Loader);
106 if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(item)) {
107 QDeclarativeItemPrivate *p =
108 static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(qmlItem));
109 p->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
110 // We may override the item's size, so we need to remember
111 // whether the item provided its own valid size.
112 itemWidthValid = p->widthValid;
113 itemHeightValid = p->heightValid;
114 } else if (item && item->isWidget()) {
115 QGraphicsWidget *widget = static_cast<QGraphicsWidget*>(item);
116 widget->installEventFilter(q);
122 \qmlclass Loader QDeclarative1Loader
123 \inqmlmodule QtQuick 1
124 \ingroup qml-utility-elements
128 \brief The Loader item allows dynamically loading an Item-based
129 subtree from a URL or Component.
131 Loader is used to dynamically load visual QML components. It can load a
132 QML file (using the \l source property) or a \l Component object (using
133 the \l sourceComponent property). It is useful for delaying the creation
134 of a component until it is required: for example, when a component should
135 be created on demand, or when a component should not be created
136 unnecessarily for performance reasons.
138 Here is a Loader that loads "Page1.qml" as a component when the
139 \l MouseArea is clicked:
141 \snippet doc/src/snippets/qtquick1/loader/simple.qml 0
143 The loaded item can be accessed using the \l item property.
145 If the \l source or \l sourceComponent changes, any previously instantiated
146 items are destroyed. Setting \l source to an empty string or setting
147 \l sourceComponent to \c undefined destroys the currently loaded item,
148 freeing resources and leaving the Loader empty.
150 \section2 Loader sizing behavior
152 Loader is like any other visual item and must be positioned and sized
153 accordingly to become visible.
156 \o If an explicit size is not specified for the Loader, the Loader
157 is automatically resized to the size of the loaded item once the
159 \o If the size of the Loader is specified explicitly by setting
160 the width, height or by anchoring, the loaded item will be resized
161 to the size of the Loader.
164 In both scenarios the size of the item and the Loader are identical.
165 This ensures that anchoring to the Loader is equivalent to anchoring
173 \o \snippet doc/src/snippets/qtquick1/loader/sizeloader.qml 0
174 \o \snippet doc/src/snippets/qtquick1/loader/sizeitem.qml 0
176 \o The red rectangle will be sized to the size of the root item.
177 \o The red rectangle will be 50x50, centered in the root item.
181 \section2 Receiving signals from loaded items
183 Any signals emitted from the loaded item can be received using the
184 \l Connections element. For example, the following \c application.qml
185 loads \c MyItem.qml, and is able to receive the \c message signal from
186 the loaded item through a \l Connections object:
193 \o \snippet doc/src/snippets/qtquick1/loader/connections.qml 0
194 \o \snippet doc/src/snippets/qtquick1/loader/MyItem.qml 0
197 Alternatively, since \c MyItem.qml is loaded within the scope of the
198 Loader, it could also directly call any function defined in the Loader or
202 \section2 Focus and key events
204 Loader is a focus scope. Its \l {Item::}{focus} property must be set to
205 \c true for any of its children to get the \e {active focus}. (See
206 \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page}
207 for more details.) Any key events received in the loaded item should likely
208 also be \l {KeyEvent::}{accepted} so they are not propagated to the Loader.
210 For example, the following \c application.qml loads \c KeyReader.qml when
211 the \l MouseArea is clicked. Notice the \l {Item::}{focus} property is
212 set to \c true for the Loader as well as the \l Item in the dynamically
220 \o \snippet doc/src/snippets/qtquick1/loader/focus.qml 0
221 \o \snippet doc/src/snippets/qtquick1/loader/KeyReader.qml 0
224 Once \c KeyReader.qml is loaded, it accepts key events and sets
225 \c event.accepted to \c true so that the event is not propagated to the
228 \sa {dynamic-object-creation}{Dynamic Object Creation}
231 QDeclarative1Loader::QDeclarative1Loader(QDeclarativeItem *parent)
232 : QDeclarative1ImplicitSizeItem(*(new QDeclarative1LoaderPrivate), parent)
234 Q_D(QDeclarative1Loader);
235 d->flags |= QGraphicsItem::ItemIsFocusScope;
238 QDeclarative1Loader::~QDeclarative1Loader()
240 Q_D(QDeclarative1Loader);
242 if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(d->item)) {
243 QDeclarativeItemPrivate *p =
244 static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(qmlItem));
245 p->removeItemChangeListener(d, QDeclarativeItemPrivate::Geometry);
251 \qmlproperty url QtQuick1::Loader::source
252 This property holds the URL of the QML component to instantiate.
254 Note the QML component must be an \l{Item}-based component. The loader
255 cannot load non-visual components.
257 To unload the currently loaded item, set this property to an empty string,
258 or set \l sourceComponent to \c undefined. Setting \c source to a
259 new URL will also cause the item created by the previous URL to be unloaded.
261 \sa sourceComponent, status, progress
263 QUrl QDeclarative1Loader::source() const
265 Q_D(const QDeclarative1Loader);
269 void QDeclarative1Loader::setSource(const QUrl &url)
271 Q_D(QDeclarative1Loader);
272 if (d->source == url)
279 if (d->source.isEmpty()) {
280 emit sourceChanged();
281 emit statusChanged();
282 emit progressChanged();
287 d->component = new QDeclarativeComponent(qmlEngine(this), d->source, this);
288 d->ownComponent = true;
290 if (isComponentComplete())
295 \qmlproperty Component QtQuick1::Loader::sourceComponent
296 This property holds the \l{Component} to instantiate.
302 Rectangle { color: "red"; width: 10; height: 10 }
305 Loader { sourceComponent: redSquare }
306 Loader { sourceComponent: redSquare; x: 10 }
310 To unload the currently loaded item, set this property to an empty string
316 QDeclarativeComponent *QDeclarative1Loader::sourceComponent() const
318 Q_D(const QDeclarative1Loader);
322 void QDeclarative1Loader::setSourceComponent(QDeclarativeComponent *comp)
324 Q_D(QDeclarative1Loader);
325 if (comp == d->component)
331 d->ownComponent = false;
334 emit sourceChanged();
335 emit statusChanged();
336 emit progressChanged();
341 if (isComponentComplete())
345 void QDeclarative1Loader::resetSourceComponent()
347 setSourceComponent(0);
350 void QDeclarative1LoaderPrivate::load()
352 Q_Q(QDeclarative1Loader);
354 if (!q->isComponentComplete() || !component)
357 if (!component->isLoading()) {
360 QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Status)),
361 q, SLOT(_q_sourceLoaded()));
362 QObject::connect(component, SIGNAL(progressChanged(qreal)),
363 q, SIGNAL(progressChanged()));
364 emit q->statusChanged();
365 emit q->progressChanged();
366 emit q->sourceChanged();
367 emit q->itemChanged();
371 void QDeclarative1LoaderPrivate::_q_sourceLoaded()
373 Q_Q(QDeclarative1Loader);
376 if (!component->errors().isEmpty()) {
377 QDeclarativeEnginePrivate::warning(qmlEngine(q), component->errors());
378 emit q->sourceChanged();
379 emit q->statusChanged();
380 emit q->progressChanged();
384 QDeclarativeContext *creationContext = component->creationContext();
385 if (!creationContext) creationContext = qmlContext(q);
386 QDeclarativeContext *ctxt = new QDeclarativeContext(creationContext);
387 ctxt->setContextObject(q);
389 QDeclarativeGuard<QDeclarativeComponent> c = component;
390 QObject *obj = component->beginCreate(ctxt);
391 if (component != c) {
392 // component->create could trigger a change in source that causes
393 // component to be set to something else. In that case we just
402 item = qobject_cast<QGraphicsObject *>(obj);
404 QDeclarative_setParent_noEvent(ctxt, obj);
405 QDeclarative_setParent_noEvent(item, q);
406 item->setParentItem(q);
407 // item->setFocus(true);
410 qmlInfo(q) << QDeclarative1Loader::tr("Loader does not support loading non-visual elements.");
415 if (!component->errors().isEmpty())
416 QDeclarativeEnginePrivate::warning(qmlEngine(q), component->errors());
421 component->completeCreate();
422 emit q->sourceChanged();
423 emit q->statusChanged();
424 emit q->progressChanged();
425 emit q->itemChanged();
431 \qmlproperty enumeration QtQuick1::Loader::status
433 This property holds the status of QML loading. It can be one of:
435 \o Loader.Null - no QML source has been set
436 \o Loader.Ready - the QML source has been loaded
437 \o Loader.Loading - the QML source is currently being loaded
438 \o Loader.Error - an error occurred while loading the QML source
441 Use this status to provide an update or respond to the status change in some way.
442 For example, you could:
445 \o Trigger a state change:
447 State { name: 'loaded'; when: loader.status == Loader.Ready }
450 \o Implement an \c onStatusChanged signal handler:
454 onStatusChanged: if (loader.status == Loader.Ready) console.log('Loaded')
458 \o Bind to the status value:
460 Text { text: loader.status == Loader.Ready ? 'Loaded' : 'Not loaded' }
464 Note that if the source is a local file, the status will initially be Ready (or Error). While
465 there will be no onStatusChanged signal in that case, the onLoaded will still be invoked.
470 QDeclarative1Loader::Status QDeclarative1Loader::status() const
472 Q_D(const QDeclarative1Loader);
475 return static_cast<QDeclarative1Loader::Status>(d->component->status());
480 return d->source.isEmpty() ? Null : Error;
483 void QDeclarative1Loader::componentComplete()
485 Q_D(QDeclarative1Loader);
487 QDeclarativeItem::componentComplete();
493 \qmlsignal QtQuick1::Loader::onLoaded()
495 This handler is called when the \l status becomes \c Loader.Ready, or on successful
501 \qmlproperty real QtQuick1::Loader::progress
503 This property holds the progress of loading QML data from the network, from
504 0.0 (nothing loaded) to 1.0 (finished). Most QML files are quite small, so
505 this value will rapidly change from 0 to 1.
509 qreal QDeclarative1Loader::progress() const
511 Q_D(const QDeclarative1Loader);
517 return d->component->progress();
522 void QDeclarative1LoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
524 Q_Q(QDeclarative1Loader);
525 if (!item || updatingSize)
529 if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(item)) {
531 q->setImplicitWidth(qmlItem->implicitWidth());
533 q->setImplicitWidth(qmlItem->width());
534 if (loaderGeometryChanged && q->widthValid())
535 qmlItem->setWidth(q->width());
536 if (!itemHeightValid)
537 q->setImplicitHeight(qmlItem->implicitHeight());
539 q->setImplicitHeight(qmlItem->height());
540 if (loaderGeometryChanged && q->heightValid())
541 qmlItem->setHeight(q->height());
542 } else if (item && item->isWidget()) {
543 QGraphicsWidget *widget = static_cast<QGraphicsWidget*>(item);
544 QSizeF widgetSize = widget->size();
545 q->setImplicitWidth(widgetSize.width());
546 if (loaderGeometryChanged && q->widthValid())
547 widgetSize.setWidth(q->width());
548 q->setImplicitHeight(widgetSize.height());
549 if (loaderGeometryChanged && q->heightValid())
550 widgetSize.setHeight(q->height());
551 if (widget->size() != widgetSize)
552 widget->resize(widgetSize);
554 updatingSize = false;
558 \qmlproperty Item QtQuick1::Loader::item
559 This property holds the top-level item that is currently loaded.
561 QGraphicsObject *QDeclarative1Loader::item() const
563 Q_D(const QDeclarative1Loader);
567 void QDeclarative1Loader::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
569 Q_D(QDeclarative1Loader);
570 if (newGeometry != oldGeometry) {
573 QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
576 QVariant QDeclarative1Loader::itemChange(GraphicsItemChange change, const QVariant &value)
578 Q_D(QDeclarative1Loader);
579 if (change == ItemSceneHasChanged) {
580 if (d->item && d->item->isWidget()) {
581 d->item->removeEventFilter(this);
582 d->item->installEventFilter(this);
585 return QDeclarativeItem::itemChange(change, value);
588 bool QDeclarative1Loader::eventFilter(QObject *watched, QEvent *e)
590 Q_D(QDeclarative1Loader);
591 if (watched == d->item && e->type() == QEvent::GraphicsSceneResize) {
592 if (d->item && d->item->isWidget())
593 d->_q_updateSize(false);
595 return QDeclarativeItem::eventFilter(watched, e);
598 #include <moc_qdeclarativeloader_p.cpp>