src/declarative/graphicsitems/qdeclarativeloader.cpp
changeset 30 5dc02b23752f
child 33 3e2da88830cd
equal deleted inserted replaced
29:b72c6db6890b 30:5dc02b23752f
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 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 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    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 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    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 
       
    48 QT_BEGIN_NAMESPACE
       
    49 
       
    50 QDeclarativeLoaderPrivate::QDeclarativeLoaderPrivate()
       
    51     : item(0), component(0), ownComponent(false)
       
    52 {
       
    53 }
       
    54 
       
    55 QDeclarativeLoaderPrivate::~QDeclarativeLoaderPrivate()
       
    56 {
       
    57 }
       
    58 
       
    59 void 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 
       
    66 void 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 
       
    95 void 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     \since 4.7
       
   112     \inherits Item
       
   113 
       
   114     \brief The Loader item allows dynamically loading an Item-based
       
   115     subtree from a QML URL or Component.
       
   116 
       
   117     Loader instantiates an item from a component. The component to
       
   118     instantiate may be specified directly by the \c sourceComponent
       
   119     property, or loaded from a URL via the \c source property.
       
   120 
       
   121     It is also an effective means of delaying the creation of a component
       
   122     until it is required:
       
   123     \code
       
   124     Loader { id: pageLoader }
       
   125     Rectangle {
       
   126         MouseArea { anchors.fill: parent; onClicked: pageLoader.source = "Page1.qml" }
       
   127     }
       
   128     \endcode
       
   129 
       
   130     If the Loader source is changed, any previous items instantiated
       
   131     will be destroyed.  Setting \c source to an empty string, or setting
       
   132     sourceComponent to \e undefined
       
   133     will destroy the currently instantiated items, freeing resources
       
   134     and leaving the Loader empty.  For example:
       
   135 
       
   136     \code
       
   137     pageLoader.source = ""
       
   138       or
       
   139     pageLoader.sourceComponent = undefined
       
   140     \endcode
       
   141 
       
   142     unloads "Page1.qml" and frees resources consumed by it.
       
   143 
       
   144     \sa {dynamic-object-creation}{Dynamic Object Creation}
       
   145 */
       
   146 
       
   147 /*!
       
   148     \internal
       
   149     \class QDeclarativeLoader
       
   150     \qmlclass Loader
       
   151  */
       
   152 
       
   153 /*!
       
   154     Create a new QDeclarativeLoader instance.
       
   155  */
       
   156 QDeclarativeLoader::QDeclarativeLoader(QDeclarativeItem *parent)
       
   157   : QDeclarativeItem(*(new QDeclarativeLoaderPrivate), parent)
       
   158 {
       
   159     Q_D(QDeclarativeItem);
       
   160     d->flags |= QGraphicsItem::ItemIsFocusScope;
       
   161 }
       
   162 
       
   163 /*!
       
   164     Destroy the loader instance.
       
   165  */
       
   166 QDeclarativeLoader::~QDeclarativeLoader()
       
   167 {
       
   168 }
       
   169 
       
   170 /*!
       
   171     \qmlproperty url Loader::source
       
   172     This property holds the URL of the QML component to
       
   173     instantiate.
       
   174 
       
   175     \sa sourceComponent, status, progress
       
   176 */
       
   177 QUrl QDeclarativeLoader::source() const
       
   178 {
       
   179     Q_D(const QDeclarativeLoader);
       
   180     return d->source;
       
   181 }
       
   182 
       
   183 void QDeclarativeLoader::setSource(const QUrl &url)
       
   184 {
       
   185     Q_D(QDeclarativeLoader);
       
   186     if (d->source == url)
       
   187         return;
       
   188 
       
   189     d->clear();
       
   190 
       
   191     d->source = url;
       
   192     if (d->source.isEmpty()) {
       
   193         emit sourceChanged();
       
   194         emit statusChanged();
       
   195         emit progressChanged();
       
   196         emit itemChanged();
       
   197         return;
       
   198     }
       
   199 
       
   200     d->component = new QDeclarativeComponent(qmlEngine(this), d->source, this);
       
   201     d->ownComponent = true;
       
   202     if (!d->component->isLoading()) {
       
   203         d->_q_sourceLoaded();
       
   204     } else {
       
   205         connect(d->component, SIGNAL(statusChanged(QDeclarativeComponent::Status)),
       
   206                 this, SLOT(_q_sourceLoaded()));
       
   207         connect(d->component, SIGNAL(progressChanged(qreal)),
       
   208                 this, SIGNAL(progressChanged()));
       
   209         emit statusChanged();
       
   210         emit progressChanged();
       
   211         emit sourceChanged();
       
   212         emit itemChanged();
       
   213     }
       
   214 }
       
   215 
       
   216 /*!
       
   217     \qmlproperty Component Loader::sourceComponent
       
   218     The sourceComponent property holds the \l{Component} to instantiate.
       
   219 
       
   220     \qml
       
   221     Item {
       
   222         Component {
       
   223             id: redSquare
       
   224             Rectangle { color: "red"; width: 10; height: 10 }
       
   225         }
       
   226 
       
   227         Loader { sourceComponent: redSquare }
       
   228         Loader { sourceComponent: redSquare; x: 10 }
       
   229     }
       
   230     \endqml
       
   231 
       
   232     \sa source, progress
       
   233 */
       
   234 
       
   235 QDeclarativeComponent *QDeclarativeLoader::sourceComponent() const
       
   236 {
       
   237     Q_D(const QDeclarativeLoader);
       
   238     return d->component;
       
   239 }
       
   240 
       
   241 void QDeclarativeLoader::setSourceComponent(QDeclarativeComponent *comp)
       
   242 {
       
   243     Q_D(QDeclarativeLoader);
       
   244     if (comp == d->component)
       
   245         return;
       
   246 
       
   247     d->clear();
       
   248 
       
   249     d->component = comp;
       
   250     d->ownComponent = false;
       
   251     if (!d->component) {
       
   252         emit sourceChanged();
       
   253         emit statusChanged();
       
   254         emit progressChanged();
       
   255         emit itemChanged();
       
   256         return;
       
   257     }
       
   258 
       
   259     if (!d->component->isLoading()) {
       
   260         d->_q_sourceLoaded();
       
   261     } else {
       
   262         connect(d->component, SIGNAL(statusChanged(QDeclarativeComponent::Status)),
       
   263                 this, SLOT(_q_sourceLoaded()));
       
   264         connect(d->component, SIGNAL(progressChanged(qreal)),
       
   265                 this, SIGNAL(progressChanged()));
       
   266         emit progressChanged();
       
   267         emit sourceChanged();
       
   268         emit statusChanged();
       
   269         emit itemChanged();
       
   270     }
       
   271 }
       
   272 
       
   273 void QDeclarativeLoader::resetSourceComponent()
       
   274 {
       
   275     setSourceComponent(0);
       
   276 }
       
   277 
       
   278 void QDeclarativeLoaderPrivate::_q_sourceLoaded()
       
   279 {
       
   280     Q_Q(QDeclarativeLoader);
       
   281 
       
   282     if (component) {
       
   283         if (!component->errors().isEmpty()) {
       
   284             QDeclarativeEnginePrivate::warning(qmlEngine(q), component->errors());
       
   285             emit q->sourceChanged();
       
   286             emit q->statusChanged();
       
   287             emit q->progressChanged();
       
   288             return;
       
   289         }
       
   290 
       
   291         QDeclarativeContext *ctxt = new QDeclarativeContext(qmlContext(q));
       
   292         ctxt->setContextObject(q);
       
   293 
       
   294         QDeclarativeComponent *c = component;
       
   295         QObject *obj = component->create(ctxt);
       
   296         if (component != c) {
       
   297             // component->create could trigger a change in source that causes
       
   298             // component to be set to something else. In that case we just
       
   299             // need to cleanup.
       
   300             delete obj;
       
   301             delete ctxt;
       
   302             return;
       
   303         }
       
   304         if (obj) {
       
   305             item = qobject_cast<QGraphicsObject *>(obj);
       
   306             if (item) {
       
   307                 QDeclarative_setParent_noEvent(ctxt, obj);
       
   308                 QDeclarative_setParent_noEvent(item, q);
       
   309                 item->setParentItem(q);
       
   310 //                item->setFocus(true);
       
   311                 initResize();
       
   312             } else {
       
   313                 qmlInfo(q) << QDeclarativeLoader::tr("Loader does not support loading non-visual elements.");
       
   314                 delete obj;
       
   315                 delete ctxt;
       
   316             }
       
   317         } else {
       
   318             if (!component->errors().isEmpty())
       
   319                 QDeclarativeEnginePrivate::warning(qmlEngine(q), component->errors());
       
   320             delete obj;
       
   321             delete ctxt;
       
   322             source = QUrl();
       
   323         }
       
   324         emit q->sourceChanged();
       
   325         emit q->statusChanged();
       
   326         emit q->progressChanged();
       
   327         emit q->itemChanged();
       
   328         emit q->loaded();
       
   329     }
       
   330 }
       
   331 
       
   332 /*!
       
   333     \qmlproperty enumeration Loader::status
       
   334 
       
   335     This property holds the status of QML loading.  It can be one of:
       
   336     \list
       
   337     \o Loader.Null - no QML source has been set
       
   338     \o Loader.Ready - the QML source has been loaded
       
   339     \o Loader.Loading - the QML source is currently being loaded
       
   340     \o Loader.Error - an error occurred while loading the QML source
       
   341     \endlist
       
   342 
       
   343     Note that a change in the status property does not cause anything to happen
       
   344     (although it reflects what has happened to the loader internally). If you wish
       
   345     to react to the change in status you need to do it yourself, for example in one
       
   346     of the following ways:
       
   347     \list
       
   348     \o Create a state, so that a state change occurs, e.g. State{name: 'loaded'; when: loader.status = Loader.Ready;}
       
   349     \o Do something inside the onLoaded signal handler, e.g. Loader{id: loader; onLoaded: console.log('Loaded');}
       
   350     \o Bind to the status variable somewhere, e.g. Text{text: if(loader.status!=Loader.Ready){'Not Loaded';}else{'Loaded';}}
       
   351     \endlist
       
   352     \sa progress
       
   353 
       
   354     Note that if the source is a local file, the status will initially be Ready (or Error). While
       
   355     there will be no onStatusChanged signal in that case, the onLoaded will still be invoked.
       
   356 */
       
   357 
       
   358 QDeclarativeLoader::Status QDeclarativeLoader::status() const
       
   359 {
       
   360     Q_D(const QDeclarativeLoader);
       
   361 
       
   362     if (d->component)
       
   363         return static_cast<QDeclarativeLoader::Status>(d->component->status());
       
   364 
       
   365     if (d->item)
       
   366         return Ready;
       
   367 
       
   368     return d->source.isEmpty() ? Null : Error;
       
   369 }
       
   370 
       
   371 void QDeclarativeLoader::componentComplete()
       
   372 {
       
   373     QDeclarativeItem::componentComplete();
       
   374     if (status() == Ready)
       
   375         emit loaded();
       
   376 }
       
   377 
       
   378 
       
   379 /*!
       
   380     \qmlsignal Loader::onLoaded()
       
   381 
       
   382     This handler is called when the \l status becomes Loader.Ready, or on successful
       
   383     initial load.
       
   384 */
       
   385 
       
   386 
       
   387 /*!
       
   388 \qmlproperty real Loader::progress
       
   389 
       
   390 This property holds the progress of loading QML data from the network, from
       
   391 0.0 (nothing loaded) to 1.0 (finished).  Most QML files are quite small, so
       
   392 this value will rapidly change from 0 to 1.
       
   393 
       
   394 \sa status
       
   395 */
       
   396 qreal QDeclarativeLoader::progress() const
       
   397 {
       
   398     Q_D(const QDeclarativeLoader);
       
   399 
       
   400     if (d->item)
       
   401         return 1.0;
       
   402 
       
   403     if (d->component)
       
   404         return d->component->progress();
       
   405 
       
   406     return 0.0;
       
   407 }
       
   408 
       
   409 void QDeclarativeLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
       
   410 {
       
   411     Q_Q(QDeclarativeLoader);
       
   412     if (!item)
       
   413         return;
       
   414     if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(item)) {
       
   415         q->setImplicitWidth(qmlItem->width());
       
   416         if (loaderGeometryChanged && q->widthValid())
       
   417             qmlItem->setWidth(q->width());
       
   418         q->setImplicitHeight(qmlItem->height());
       
   419         if (loaderGeometryChanged && q->heightValid())
       
   420             qmlItem->setHeight(q->height());
       
   421     } else if (item && item->isWidget()) {
       
   422         QGraphicsWidget *widget = static_cast<QGraphicsWidget*>(item);
       
   423         QSizeF widgetSize = widget->size();
       
   424         q->setImplicitWidth(widgetSize.width());
       
   425         if (loaderGeometryChanged && q->widthValid())
       
   426             widgetSize.setWidth(q->width());
       
   427         q->setImplicitHeight(widgetSize.height());
       
   428         if (loaderGeometryChanged && q->heightValid())
       
   429             widgetSize.setHeight(q->height());
       
   430         if (widget->size() != widgetSize)
       
   431             widget->resize(widgetSize);
       
   432     }
       
   433 }
       
   434 
       
   435 /*!
       
   436     \qmlproperty Item Loader::item
       
   437     This property holds the top-level item created from source.
       
   438 */
       
   439 QGraphicsObject *QDeclarativeLoader::item() const
       
   440 {
       
   441     Q_D(const QDeclarativeLoader);
       
   442     return d->item;
       
   443 }
       
   444 
       
   445 void QDeclarativeLoader::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
       
   446 {
       
   447     Q_D(QDeclarativeLoader);
       
   448     if (newGeometry != oldGeometry) {
       
   449         d->_q_updateSize();
       
   450     }
       
   451     QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
       
   452 }
       
   453 
       
   454 QVariant QDeclarativeLoader::itemChange(GraphicsItemChange change, const QVariant &value)
       
   455 {
       
   456     Q_D(QDeclarativeLoader);
       
   457     if (change == ItemSceneHasChanged) {
       
   458         if (d->item && d->item->isWidget()) {
       
   459             d->item->removeEventFilter(this);
       
   460             d->item->installEventFilter(this);
       
   461         }
       
   462     }
       
   463     return QDeclarativeItem::itemChange(change, value);
       
   464 }
       
   465 
       
   466 bool QDeclarativeLoader::eventFilter(QObject *watched, QEvent *e)
       
   467 {
       
   468     Q_D(QDeclarativeLoader);
       
   469     if (watched == d->item && e->type() == QEvent::GraphicsSceneResize) {
       
   470         if (d->item && d->item->isWidget())
       
   471             d->_q_updateSize(false);
       
   472     }
       
   473     return QDeclarativeItem::eventFilter(watched, e);
       
   474 }
       
   475 
       
   476 #include <moc_qdeclarativeloader_p.cpp>
       
   477 
       
   478 QT_END_NAMESPACE