Allow Loader to load non-Item types.

Loader has a more convenient API for loading/unloading components
than the dynamic object creation APIs.  Remove the Item-only
restriction.

Change-Id: I6f9ecc8514ff1e814f7e56a3386814ba211b7e4f
Reviewed-by: Andrew den Exter <andrew.den-exter@nokia.com>
This commit is contained in:
Martin Jones 2012-06-12 10:16:47 +10:00 committed by Qt by Nokia
parent fb41baa0e8
commit e8206bf6ab
4 changed files with 56 additions and 41 deletions

View File

@ -56,7 +56,7 @@ static const QQuickItemPrivate::ChangeTypes watchedChanges
= QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight; = QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
QQuickLoaderPrivate::QQuickLoaderPrivate() QQuickLoaderPrivate::QQuickLoaderPrivate()
: item(0), component(0), itemContext(0), incubator(0), updatingSize(false), : item(0), object(0), component(0), itemContext(0), incubator(0), updatingSize(false),
active(true), loadingFromSource(false), asynchronous(false) active(true), loadingFromSource(false), asynchronous(false)
{ {
} }
@ -118,13 +118,18 @@ void QQuickLoaderPrivate::clear()
// the Loader to load a different item. // the Loader to load a different item.
item->setParentItem(0); item->setParentItem(0);
item->setVisible(false); item->setVisible(false);
item->deleteLater();
item = 0; item = 0;
} }
if (object) {
object->deleteLater();
object = 0;
}
} }
void QQuickLoaderPrivate::initResize() void QQuickLoaderPrivate::initResize()
{ {
if (!item)
return;
QQuickItemPrivate *p = QQuickItemPrivate::get(item); QQuickItemPrivate *p = QQuickItemPrivate::get(item);
p->addItemChangeListener(this, watchedChanges); p->addItemChangeListener(this, watchedChanges);
_q_updateSize(); _q_updateSize();
@ -158,10 +163,9 @@ qreal QQuickLoaderPrivate::getImplicitHeight() const
\ingroup qtquick-utility \ingroup qtquick-utility
\inherits Item \inherits Item
\brief Allows dynamical loading of an item-based subtree from a URL or Component \brief Allows dynamic loading of a subtree from a URL or Component
Loader is used to dynamically load visual QML components. For loading non-visual Loader is used to dynamically load QML components.
components, see \l {Dynamic Object Management in QML}.
Loader can load a Loader can load a
QML file (using the \l source property) or a \l Component object (using QML file (using the \l source property) or a \l Component object (using
@ -175,17 +179,18 @@ qreal QQuickLoaderPrivate::getImplicitHeight() const
\snippet qml/loader/simple.qml 0 \snippet qml/loader/simple.qml 0
The loaded item can be accessed using the \l item property. The loaded object can be accessed using the \l item property.
If the \l source or \l sourceComponent changes, any previously instantiated If the \l source or \l sourceComponent changes, any previously instantiated
items are destroyed. Setting \l source to an empty string or setting items are destroyed. Setting \l source to an empty string or setting
\l sourceComponent to \c undefined destroys the currently loaded item, \l sourceComponent to \c undefined destroys the currently loaded object,
freeing resources and leaving the Loader empty. freeing resources and leaving the Loader empty.
\section2 Loader sizing behavior \section2 Loader sizing behavior
Loader is like any other visual item and must be positioned and sized If the source component is not an Item type, Loader does not
accordingly to become visible. apply any special sizing rules. When used to load visual types,
Loader applies the following sizing rules:
\list \list
\li If an explicit size is not specified for the Loader, the Loader \li If an explicit size is not specified for the Loader, the Loader
@ -213,9 +218,9 @@ qreal QQuickLoaderPrivate::getImplicitHeight() const
\endtable \endtable
\section2 Receiving signals from loaded items \section2 Receiving signals from loaded objects
Any signals emitted from the loaded item can be received using the Any signals emitted from the loaded object can be received using the
\l Connections element. For example, the following \c application.qml \l Connections element. For example, the following \c application.qml
loads \c MyItem.qml, and is able to receive the \c message signal from loads \c MyItem.qml, and is able to receive the \c message signal from
the loaded item through a \l Connections object: the loaded item through a \l Connections object:
@ -260,6 +265,8 @@ qreal QQuickLoaderPrivate::getImplicitHeight() const
\c event.accepted to \c true so that the event is not propagated to the \c event.accepted to \c true so that the event is not propagated to the
parent \l Rectangle. parent \l Rectangle.
Since QtQuick 2.0 Loader can also load non-visual components.
\sa {dynamic-object-creation}{Dynamic Object Creation} \sa {dynamic-object-creation}{Dynamic Object Creation}
*/ */
@ -326,8 +333,11 @@ void QQuickLoader::setActive(bool newVal)
// the Loader to load a different item. // the Loader to load a different item.
d->item->setParentItem(0); d->item->setParentItem(0);
d->item->setVisible(false); d->item->setVisible(false);
d->item->deleteLater();
d->item = 0; d->item = 0;
}
if (d->object) {
d->object->deleteLater();
d->object = 0;
emit itemChanged(); emit itemChanged();
} }
emit statusChanged(); emit statusChanged();
@ -341,10 +351,10 @@ void QQuickLoader::setActive(bool newVal)
\qmlproperty url QtQuick2::Loader::source \qmlproperty url QtQuick2::Loader::source
This property holds the URL of the QML component to instantiate. This property holds the URL of the QML component to instantiate.
Note the QML component must be an \l{Item}-based component. The loader Since QtQuick 2.0 Loader is able to load any type of object; it
cannot load non-visual components. is not restricted to Item types.
To unload the currently loaded item, set this property to an empty string, To unload the currently loaded object, set this property to an empty string,
or set \l sourceComponent to \c undefined. Setting \c source to a or set \l sourceComponent to \c undefined. Setting \c source to a
new URL will also cause the item created by the previous URL to be unloaded. new URL will also cause the item created by the previous URL to be unloaded.
@ -413,9 +423,12 @@ void QQuickLoader::loadFromSource()
} }
\endqml \endqml
To unload the currently loaded item, set this property to an empty string To unload the currently loaded object, set this property to an empty string
or \c undefined. or \c undefined.
Since QtQuick 2.0 Loader is able to load any type of object; it
is not restricted to Item types.
\sa source, progress \sa source, progress
*/ */
@ -597,9 +610,11 @@ void QQuickLoaderPrivate::setInitialState(QObject *obj)
item->setWidth(q->width()); item->setWidth(q->width());
if (heightValid && !QQuickItemPrivate::get(item)->heightValid) if (heightValid && !QQuickItemPrivate::get(item)->heightValid)
item->setHeight(q->height()); item->setHeight(q->height());
QQml_setParent_noEvent(itemContext, obj);
QQml_setParent_noEvent(item, q);
item->setParentItem(q); item->setParentItem(q);
}
if (obj) {
QQml_setParent_noEvent(itemContext, obj);
QQml_setParent_noEvent(obj, q);
itemContext = 0; itemContext = 0;
} }
@ -623,18 +638,10 @@ void QQuickLoaderPrivate::incubatorStateChanged(QQmlIncubator::Status status)
return; return;
if (status == QQmlIncubator::Ready) { if (status == QQmlIncubator::Ready) {
QObject *obj = incubator->object(); object = incubator->object();
item = qmlobject_cast<QQuickItem*>(obj); item = qmlobject_cast<QQuickItem*>(object);
if (item) { emit q->itemChanged();
emit q->itemChanged(); initResize();
initResize();
} else {
qmlInfo(q) << QQuickLoader::tr("Loader does not support loading non-visual elements.");
delete itemContext;
itemContext = 0;
delete obj;
emit q->itemChanged();
}
incubator->clear(); incubator->clear();
} else if (status == QQmlIncubator::Error) { } else if (status == QQmlIncubator::Error) {
if (!incubator->errors().isEmpty()) if (!incubator->errors().isEmpty())
@ -757,7 +764,7 @@ QQuickLoader::Status QQuickLoader::status() const
} }
} }
if (d->item) if (d->object)
return Ready; return Ready;
return d->source.isEmpty() ? Null : Error; return d->source.isEmpty() ? Null : Error;
@ -797,7 +804,7 @@ qreal QQuickLoader::progress() const
{ {
Q_D(const QQuickLoader); Q_D(const QQuickLoader);
if (d->item) if (d->object)
return 1.0; return 1.0;
if (d->component) if (d->component)
@ -868,13 +875,15 @@ void QQuickLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
} }
/*! /*!
\qmlproperty Item QtQuick2::Loader::item \qmlproperty object QtQuick2::Loader::item
This property holds the top-level item that is currently loaded. This property holds the top-level object that is currently loaded.
Since QtQuick 2.0 Loader can load any object type.
*/ */
QQuickItem *QQuickLoader::item() const QObject *QQuickLoader::item() const
{ {
Q_D(const QQuickLoader); Q_D(const QQuickLoader);
return d->item; return d->object;
} }
void QQuickLoader::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) void QQuickLoader::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)

View File

@ -57,7 +57,7 @@ class Q_AUTOTEST_EXPORT QQuickLoader : public QQuickImplicitSizeItem
Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged) Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
Q_PROPERTY(QQmlComponent *sourceComponent READ sourceComponent WRITE setSourceComponent RESET resetSourceComponent NOTIFY sourceComponentChanged) Q_PROPERTY(QQmlComponent *sourceComponent READ sourceComponent WRITE setSourceComponent RESET resetSourceComponent NOTIFY sourceComponentChanged)
Q_PROPERTY(QQuickItem *item READ item NOTIFY itemChanged) Q_PROPERTY(QObject *item READ item NOTIFY itemChanged)
Q_PROPERTY(Status status READ status NOTIFY statusChanged) Q_PROPERTY(Status status READ status NOTIFY statusChanged)
Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged) Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged)
Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged)
@ -85,7 +85,7 @@ public:
bool asynchronous() const; bool asynchronous() const;
void setAsynchronous(bool a); void setAsynchronous(bool a);
QQuickItem *item() const; QObject *item() const;
Q_SIGNALS: Q_SIGNALS:
void itemChanged(); void itemChanged();

View File

@ -104,6 +104,7 @@ public:
QUrl source; QUrl source;
QQuickItem *item; QQuickItem *item;
QObject *object;
QQmlComponent *component; QQmlComponent *component;
QQmlContext *itemContext; QQmlContext *itemContext;
QQuickLoaderIncubator *incubator; QQuickLoaderIncubator *incubator;

View File

@ -783,12 +783,17 @@ void tst_QQuickLoader::deleteComponentCrash()
void tst_QQuickLoader::nonItem() void tst_QQuickLoader::nonItem()
{ {
QQmlComponent component(&engine, testFileUrl("nonItem.qml")); QQmlComponent component(&engine, testFileUrl("nonItem.qml"));
QString err = testFileUrl("nonItem.qml").toString() + ":3:1: QML Loader: Loader does not support loading non-visual elements.";
QTest::ignoreMessage(QtWarningMsg, err.toLatin1().constData());
QQuickLoader *loader = qobject_cast<QQuickLoader*>(component.create()); QQuickLoader *loader = qobject_cast<QQuickLoader*>(component.create());
QVERIFY(loader); QVERIFY(loader);
QVERIFY(loader->item() == 0); QVERIFY(loader->item());
QCOMPARE(loader, loader->item()->parent());
QPointer<QObject> item = loader->item();
loader->setActive(false);
QVERIFY(!loader->item());
QTRY_VERIFY(!item);
delete loader; delete loader;
} }