Allow Loader to load non-Item types.
authorMartin Jones <martin.jones@nokia.com>
Tue, 12 Jun 2012 00:16:47 +0000 (10:16 +1000)
committerQt by Nokia <qt-info@nokia.com>
Tue, 12 Jun 2012 01:22:11 +0000 (03:22 +0200)
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>
src/quick/items/qquickloader.cpp
src/quick/items/qquickloader_p.h
src/quick/items/qquickloader_p_p.h
tests/auto/quick/qquickloader/tst_qquickloader.cpp

index 5406016..04e248e 100644 (file)
@@ -56,7 +56,7 @@ static const QQuickItemPrivate::ChangeTypes watchedChanges
     = QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
 
 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)
 {
 }
@@ -118,13 +118,18 @@ void QQuickLoaderPrivate::clear()
         // the Loader to load a different item.
         item->setParentItem(0);
         item->setVisible(false);
-        item->deleteLater();
         item = 0;
     }
+    if (object) {
+        object->deleteLater();
+        object = 0;
+    }
 }
 
 void QQuickLoaderPrivate::initResize()
 {
+    if (!item)
+        return;
     QQuickItemPrivate *p = QQuickItemPrivate::get(item);
     p->addItemChangeListener(this, watchedChanges);
     _q_updateSize();
@@ -158,10 +163,9 @@ qreal QQuickLoaderPrivate::getImplicitHeight() const
     \ingroup qtquick-utility
     \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
-    components, see \l {Dynamic Object Management in QML}.
+    Loader is used to dynamically load QML components.
 
     Loader can load a
     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
 
-    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
     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.
 
     \section2 Loader sizing behavior
 
-    Loader is like any other visual item and must be positioned and sized
-    accordingly to become visible.
+    If the source component is not an Item type, Loader does not
+    apply any special sizing rules.  When used to load visual types,
+    Loader applies the following sizing rules:
 
     \list
     \li If an explicit size is not specified for the Loader, the Loader
@@ -213,9 +218,9 @@ qreal QQuickLoaderPrivate::getImplicitHeight() const
     \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
     loads \c MyItem.qml, and is able to receive the \c message signal from
     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
     parent \l Rectangle.
 
+    Since QtQuick 2.0 Loader can also load non-visual components.
+
     \sa {dynamic-object-creation}{Dynamic Object Creation}
 */
 
@@ -326,8 +333,11 @@ void QQuickLoader::setActive(bool newVal)
                 // the Loader to load a different item.
                 d->item->setParentItem(0);
                 d->item->setVisible(false);
-                d->item->deleteLater();
                 d->item = 0;
+            }
+            if (d->object) {
+                d->object->deleteLater();
+                d->object = 0;
                 emit itemChanged();
             }
             emit statusChanged();
@@ -341,10 +351,10 @@ void QQuickLoader::setActive(bool newVal)
     \qmlproperty url QtQuick2::Loader::source
     This property holds the URL of the QML component to instantiate.
 
-    Note the QML component must be an \l{Item}-based component. The loader
-    cannot load non-visual components.
+    Since QtQuick 2.0 Loader is able to load any type of object; it
+    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
     new URL will also cause the item created by the previous URL to be unloaded.
 
@@ -413,9 +423,12 @@ void QQuickLoader::loadFromSource()
     }
     \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.
 
+    Since QtQuick 2.0 Loader is able to load any type of object; it
+    is not restricted to Item types.
+
     \sa source, progress
 */
 
@@ -597,9 +610,11 @@ void QQuickLoaderPrivate::setInitialState(QObject *obj)
             item->setWidth(q->width());
         if (heightValid && !QQuickItemPrivate::get(item)->heightValid)
             item->setHeight(q->height());
-        QQml_setParent_noEvent(itemContext, obj);
-        QQml_setParent_noEvent(item, q);
         item->setParentItem(q);
+    }
+    if (obj) {
+        QQml_setParent_noEvent(itemContext, obj);
+        QQml_setParent_noEvent(obj, q);
         itemContext = 0;
     }
 
@@ -623,18 +638,10 @@ void QQuickLoaderPrivate::incubatorStateChanged(QQmlIncubator::Status status)
         return;
 
     if (status == QQmlIncubator::Ready) {
-        QObject *obj = incubator->object();
-        item = qmlobject_cast<QQuickItem*>(obj);
-        if (item) {
-            emit q->itemChanged();
-            initResize();
-        } else {
-            qmlInfo(q) << QQuickLoader::tr("Loader does not support loading non-visual elements.");
-            delete itemContext;
-            itemContext = 0;
-            delete obj;
-            emit q->itemChanged();
-        }
+        object = incubator->object();
+        item = qmlobject_cast<QQuickItem*>(object);
+        emit q->itemChanged();
+        initResize();
         incubator->clear();
     } else if (status == QQmlIncubator::Error) {
         if (!incubator->errors().isEmpty())
@@ -757,7 +764,7 @@ QQuickLoader::Status QQuickLoader::status() const
         }
     }
 
-    if (d->item)
+    if (d->object)
         return Ready;
 
     return d->source.isEmpty() ? Null : Error;
@@ -797,7 +804,7 @@ qreal QQuickLoader::progress() const
 {
     Q_D(const QQuickLoader);
 
-    if (d->item)
+    if (d->object)
         return 1.0;
 
     if (d->component)
@@ -868,13 +875,15 @@ void QQuickLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
 }
 
 /*!
-    \qmlproperty Item QtQuick2::Loader::item
-    This property holds the top-level item that is currently loaded.
+    \qmlproperty object QtQuick2::Loader::item
+    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);
-    return d->item;
+    return d->object;
 }
 
 void QQuickLoader::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
index c5f544b..ff6e897 100644 (file)
@@ -57,7 +57,7 @@ class Q_AUTOTEST_EXPORT QQuickLoader : public QQuickImplicitSizeItem
     Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
     Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
     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(qreal progress READ progress NOTIFY progressChanged)
     Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged)
@@ -85,7 +85,7 @@ public:
     bool asynchronous() const;
     void setAsynchronous(bool a);
 
-    QQuickItem *item() const;
+    QObject *item() const;
 
 Q_SIGNALS:
     void itemChanged();
index 0978911..2f82fa9 100644 (file)
@@ -104,6 +104,7 @@ public:
 
     QUrl source;
     QQuickItem *item;
+    QObject *object;
     QQmlComponent *component;
     QQmlContext *itemContext;
     QQuickLoaderIncubator *incubator;
index 69c75d6..e7cef8c 100644 (file)
@@ -783,12 +783,17 @@ void tst_QQuickLoader::deleteComponentCrash()
 void tst_QQuickLoader::nonItem()
 {
     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());
     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;
 }