Update to 5.0.0-beta1
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickview.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquickview.h"
43 #include "qquickview_p.h"
44
45 #include "qquickwindow_p.h"
46 #include "qquickitem_p.h"
47 #include "qquickitemchangelistener_p.h"
48
49 #include <private/qqmlprofilerservice_p.h>
50 #include <private/qqmlinspectorservice_p.h>
51 #include <private/qqmlmemoryprofiler_p.h>
52
53 #include <QtQml/qqmlengine.h>
54 #include <private/qqmlengine_p.h>
55 #include <QtCore/qbasictimer.h>
56
57 QT_BEGIN_NAMESPACE
58
59 void QQuickViewPrivate::init(QQmlEngine* e)
60 {
61     Q_Q(QQuickView);
62
63     engine = e;
64
65     if (engine.isNull())
66         engine = new QQmlEngine(q);
67
68     if (!engine.data()->incubationController())
69         engine.data()->setIncubationController(q->incubationController());
70
71     if (QQmlDebugService::isDebuggingEnabled())
72         QQmlInspectorService::instance()->addView(q);
73 }
74
75 QQuickViewPrivate::QQuickViewPrivate()
76     : root(0), component(0), resizeMode(QQuickView::SizeViewToRootObject), initialSize(0,0)
77 {
78 }
79
80 QQuickViewPrivate::~QQuickViewPrivate()
81 {
82     if (QQmlDebugService::isDebuggingEnabled())
83         QQmlInspectorService::instance()->removeView(q_func());
84 }
85
86 void QQuickViewPrivate::execute()
87 {
88     Q_Q(QQuickView);
89     if (!engine) {
90         qWarning() << "QQuickView: invalid qml engine.";
91         return;
92     }
93
94     if (root) {
95         delete root;
96         root = 0;
97     }
98     if (component) {
99         delete component;
100         component = 0;
101     }
102     if (!source.isEmpty()) {
103         QML_MEMORY_SCOPE_URL(engine.data()->baseUrl().resolved(source));
104         component = new QQmlComponent(engine.data(), source, q);
105         if (!component->isLoading()) {
106             q->continueExecute();
107         } else {
108             QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)),
109                              q, SLOT(continueExecute()));
110         }
111     }
112 }
113
114 void QQuickViewPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry)
115 {
116     Q_Q(QQuickView);
117     if (resizeItem == root && resizeMode == QQuickView::SizeViewToRootObject) {
118         // wait for both width and height to be changed
119         resizetimer.start(0,q);
120     }
121     QQuickItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry);
122 }
123
124 /*!
125     \class QQuickView
126     \since QtQuick 2.0
127     \brief The QQuickView class provides a window for displaying a Qt Quick user interface.
128
129     \inmodule QtQuick
130
131     This is a convenience subclass of QQuickWindow which
132     will automatically load and display a QML scene when given the URL of the main source file. Alternatively,
133     you can instantiate your own objects using QQmlComponent and place them in a manually setup QQuickWindow.
134
135     Typical usage:
136
137     \code
138     QQuickView *view = new QQuickView;
139     view->setSource(QUrl::fromLocalFile("myqmlfile.qml"));
140     view->show();
141     \endcode
142
143     To receive errors related to loading and executing QML with QQuickView,
144     you can connect to the statusChanged() signal and monitor for QQuickView::Error.
145     The errors are available via QQuickView::errors().
146
147     QQuickView also manages sizing of the view and root object.  By default, the \l resizeMode
148     is SizeViewToRootObject, which will load the component and resize it to the
149     size of the view.  Alternatively the resizeMode may be set to SizeRootObjectToView which
150     will resize the view to the size of the root object.
151
152     \sa {qtqml-cppintegration-exposecppattributes.html}{Exposing Attributes of C++ Types to QML}
153 */
154
155
156 /*! \fn void QQuickView::sceneResized(QSize size)
157   This signal is emitted when the view is resized to \a size.
158 */
159
160 /*! \fn void QQuickView::statusChanged(QQuickView::Status status)
161     This signal is emitted when the component's current \a status changes.
162 */
163
164 /*! \fn void QQuickView::initialSizeChanged(QSize size)
165   \internal
166 */
167
168 /*!
169   Constructs a QQuickView with the given \a parent.
170   The default value of \a parent is 0.
171
172 */
173 QQuickView::QQuickView(QWindow *parent)
174 : QQuickWindow(*(new QQuickViewPrivate), parent)
175 {
176     d_func()->init();
177 }
178
179 /*!
180   Constructs a QQuickView with the given QML \a source and \a parent.
181   The default value of \a parent is 0.
182
183 */
184 QQuickView::QQuickView(const QUrl &source, QWindow *parent)
185 : QQuickWindow(*(new QQuickViewPrivate), parent)
186 {
187     d_func()->init();
188     setSource(source);
189 }
190
191 /*!
192   Constructs a QQuickView with the given QML \a engine and \a parent.
193
194   Note: In this case, the QQuickView does not own the given \a engine object;
195   it is the caller's responsibility to destroy the engine. If the \a engine is deleted
196   before the view \a status() will return \a QQuickView::Error.
197
198   \sa Status, status(), errors()
199 */
200 QQuickView::QQuickView(QQmlEngine* engine, QWindow *parent)
201     : QQuickWindow(*(new QQuickViewPrivate), parent)
202 {
203     Q_ASSERT(engine);
204     d_func()->init(engine);
205 }
206
207 /*!
208   Destroys the QQuickView.
209 */
210 QQuickView::~QQuickView()
211 {
212     // Ensure that the component is destroyed before the engine; the engine may
213     // be a child of the QQuickViewPrivate, and will be destroyed by its dtor
214     Q_D(QQuickView);
215     delete d->root;
216     d->root = 0;
217 }
218
219 /*!
220   \property QQuickView::source
221   \brief The URL of the source of the QML component.
222
223   Ensure that the URL provided is full and correct, in particular, use
224   \l QUrl::fromLocalFile() when loading a file from the local filesystem.
225
226   Note that setting a source URL will result in the QML component being
227   instantiated, even if the URL is unchanged from the current value.
228 */
229
230 /*!
231     Sets the source to the \a url, loads the QML component and instantiates it.
232
233     Ensure that the URL provided is full and correct, in particular, use
234     \l QUrl::fromLocalFile() when loading a file from the local filesystem.
235
236     Calling this method multiple times with the same url will result
237     in the QML component being reinstantiated.
238  */
239 void QQuickView::setSource(const QUrl& url)
240 {
241     Q_D(QQuickView);
242     d->source = url;
243     d->execute();
244 }
245
246 /*!
247   Returns the source URL, if set.
248
249   \sa setSource()
250  */
251 QUrl QQuickView::source() const
252 {
253     Q_D(const QQuickView);
254     return d->source;
255 }
256
257 /*!
258   Returns a pointer to the QQmlEngine used for instantiating
259   QML Components.
260  */
261 QQmlEngine* QQuickView::engine() const
262 {
263     Q_D(const QQuickView);
264     return d->engine ? const_cast<QQmlEngine *>(d->engine.data()) : 0;
265 }
266
267 /*!
268   This function returns the root of the context hierarchy.  Each QML
269   component is instantiated in a QQmlContext.  QQmlContext's are
270   essential for passing data to QML components.  In QML, contexts are
271   arranged hierarchically and this hierarchy is managed by the
272   QQmlEngine.
273  */
274 QQmlContext* QQuickView::rootContext() const
275 {
276     Q_D(const QQuickView);
277     return d->engine ? d->engine.data()->rootContext() : 0;
278 }
279
280 /*!
281     \enum QQuickView::Status
282     Specifies the loading status of the QQuickView.
283
284     \value Null This QQuickView has no source set.
285     \value Ready This QQuickView has loaded and created the QML component.
286     \value Loading This QQuickView is loading network data.
287     \value Error One or more errors has occurred. Call errors() to retrieve a list
288            of errors.
289 */
290
291 /*! \enum QQuickView::ResizeMode
292
293   This enum specifies how to resize the view.
294
295   \value SizeViewToRootObject The view resizes with the root item in the QML.
296   \value SizeRootObjectToView The view will automatically resize the root item to the size of the view.
297 */
298
299 /*!
300     \property QQuickView::status
301     The component's current \l{QQuickView::Status} {status}.
302 */
303
304 QQuickView::Status QQuickView::status() const
305 {
306     Q_D(const QQuickView);
307     if (!d->engine)
308         return QQuickView::Error;
309
310     if (!d->component)
311         return QQuickView::Null;
312
313     return QQuickView::Status(d->component->status());
314 }
315
316 /*!
317     Return the list of errors that occurred during the last compile or create
318     operation.  When the status is not Error, an empty list is returned.
319 */
320 QList<QQmlError> QQuickView::errors() const
321 {
322     Q_D(const QQuickView);
323     QList<QQmlError> errs;
324
325     if (d->component)
326         errs = d->component->errors();
327
328     if (!d->engine) {
329         QQmlError error;
330         error.setDescription(QLatin1String("QQuickView: invalid qml engine."));
331         errs << error;
332     }
333
334     return errs;
335 }
336
337 /*!
338     \property QQuickView::resizeMode
339     \brief whether the view should resize the window contents
340
341     If this property is set to SizeViewToRootObject (the default), the view
342     resizes to the size of the root item in the QML.
343
344     If this property is set to SizeRootObjectToView, the view will
345     automatically resize the root item to the size of the view.
346
347     Regardless of this property, the sizeHint of the view
348     is the initial size of the root item. Note though that
349     since QML may load dynamically, that size may change.
350
351     \sa initialSize()
352 */
353
354 void QQuickView::setResizeMode(ResizeMode mode)
355 {
356     Q_D(QQuickView);
357     if (d->resizeMode == mode)
358         return;
359
360     if (d->root) {
361         if (d->resizeMode == SizeViewToRootObject) {
362             QQuickItemPrivate *p = QQuickItemPrivate::get(d->root);
363             p->removeItemChangeListener(d, QQuickItemPrivate::Geometry);
364         }
365     }
366
367     d->resizeMode = mode;
368     if (d->root) {
369         d->initResize();
370     }
371 }
372
373 void QQuickViewPrivate::initResize()
374 {
375     if (root) {
376         if (resizeMode == QQuickView::SizeViewToRootObject) {
377             QQuickItemPrivate *p = QQuickItemPrivate::get(root);
378             p->addItemChangeListener(this, QQuickItemPrivate::Geometry);
379         }
380     }
381     updateSize();
382 }
383
384 void QQuickViewPrivate::updateSize()
385 {
386     Q_Q(QQuickView);
387     if (!root)
388         return;
389
390     if (resizeMode == QQuickView::SizeViewToRootObject) {
391         QSize newSize = QSize(root->width(), root->height());
392         if (newSize.isValid() && newSize != q->size()) {
393             q->resize(newSize);
394         }
395     } else if (resizeMode == QQuickView::SizeRootObjectToView) {
396         if (!qFuzzyCompare(q->width(), root->width()))
397             root->setWidth(q->width());
398         if (!qFuzzyCompare(q->height(), root->height()))
399             root->setHeight(q->height());
400     }
401 }
402
403 QSize QQuickViewPrivate::rootObjectSize() const
404 {
405     QSize rootObjectSize(0,0);
406     int widthCandidate = -1;
407     int heightCandidate = -1;
408     if (root) {
409         widthCandidate = root->width();
410         heightCandidate = root->height();
411     }
412     if (widthCandidate > 0) {
413         rootObjectSize.setWidth(widthCandidate);
414     }
415     if (heightCandidate > 0) {
416         rootObjectSize.setHeight(heightCandidate);
417     }
418     return rootObjectSize;
419 }
420
421 QQuickView::ResizeMode QQuickView::resizeMode() const
422 {
423     Q_D(const QQuickView);
424     return d->resizeMode;
425 }
426
427 /*!
428   \internal
429  */
430 void QQuickView::continueExecute()
431 {
432     Q_D(QQuickView);
433     disconnect(d->component, SIGNAL(statusChanged(QQmlComponent::Status)), this, SLOT(continueExecute()));
434
435     if (d->component->isError()) {
436         QList<QQmlError> errorList = d->component->errors();
437         foreach (const QQmlError &error, errorList) {
438             QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), 0).warning()
439                     << error;
440         }
441         emit statusChanged(status());
442         return;
443     }
444
445     QObject *obj = d->component->create();
446
447     if (d->component->isError()) {
448         QList<QQmlError> errorList = d->component->errors();
449         foreach (const QQmlError &error, errorList) {
450             QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), 0).warning()
451                     << error;
452         }
453         emit statusChanged(status());
454         return;
455     }
456
457     d->setRootObject(obj);
458     emit statusChanged(status());
459 }
460
461
462 /*!
463   \internal
464 */
465 void QQuickViewPrivate::setRootObject(QObject *obj)
466 {
467     Q_Q(QQuickView);
468     if (root == obj)
469         return;
470     if (QQuickItem *sgItem = qobject_cast<QQuickItem *>(obj)) {
471         root = sgItem;
472         sgItem->setParentItem(q->QQuickWindow::rootItem());
473     } else {
474         qWarning() << "QQuickView only supports loading of root objects that derive from QQuickItem." << endl
475                    << endl
476                    << "If your example is using QML 2, (such as qmlscene) and the .qml file you" << endl
477                    << "loaded has 'import QtQuick 1.0' or 'import Qt 4.7', this error will occur." << endl
478                    << endl
479                    << "To load files with 'import QtQuick 1.0' or 'import Qt 4.7', use the" << endl
480                    << "QQuickView class in the qtquick1 module." << endl;
481         delete obj;
482         root = 0;
483     }
484     if (root) {
485         initialSize = rootObjectSize();
486         if ((resizeMode == QQuickView::SizeViewToRootObject || q->width() <= 1 || q->height() <= 1) &&
487             initialSize != q->size()) {
488             q->resize(initialSize);
489         }
490         initResize();
491     }
492 }
493
494 /*!
495   \internal
496   If the \l {QTimerEvent} {timer event} \a e is this
497   view's resize timer, sceneResized() is emitted.
498  */
499 void QQuickView::timerEvent(QTimerEvent* e)
500 {
501     Q_D(QQuickView);
502     if (!e || e->timerId() == d->resizetimer.timerId()) {
503         d->updateSize();
504         d->resizetimer.stop();
505     }
506 }
507
508 /*!
509     \internal
510     Preferred size follows the root object geometry.
511 */
512 QSize QQuickView::sizeHint() const
513 {
514     Q_D(const QQuickView);
515     QSize rootObjectSize = d->rootObjectSize();
516     if (rootObjectSize.isEmpty()) {
517         return size();
518     } else {
519         return rootObjectSize;
520     }
521 }
522
523 /*!
524   Returns the initial size of the root object.
525
526   If \l resizeMode is QQuickItem::SizeRootObjectToView the root object will be
527   resized to the size of the view.  initialSize contains the size of the
528   root object before it was resized.
529 */
530 QSize QQuickView::initialSize() const
531 {
532     Q_D(const QQuickView);
533     return d->initialSize;
534 }
535
536 /*!
537   Returns the view's root \l {QQuickItem} {item}.
538  */
539 QQuickItem *QQuickView::rootObject() const
540 {
541     Q_D(const QQuickView);
542     return d->root;
543 }
544
545 /*!
546   \internal
547   This function handles the \l {QResizeEvent} {resize event}
548   \a e.
549  */
550 void QQuickView::resizeEvent(QResizeEvent *e)
551 {
552     Q_D(QQuickView);
553     if (d->resizeMode == SizeRootObjectToView)
554         d->updateSize();
555
556     QQuickWindow::resizeEvent(e);
557 }
558
559 /*! \reimp */
560 void QQuickView::keyPressEvent(QKeyEvent *e)
561 {
562     QQmlProfilerService::addEvent(QQmlProfilerService::Key);
563
564     QQuickWindow::keyPressEvent(e);
565 }
566
567 /*! \reimp */
568 void QQuickView::keyReleaseEvent(QKeyEvent *e)
569 {
570     QQmlProfilerService::addEvent(QQmlProfilerService::Key);
571
572     QQuickWindow::keyReleaseEvent(e);
573 }
574
575 /*! \reimp */
576 void QQuickView::mouseMoveEvent(QMouseEvent *e)
577 {
578     QQmlProfilerService::addEvent(QQmlProfilerService::Mouse);
579
580     QQuickWindow::mouseMoveEvent(e);
581 }
582
583 /*! \reimp */
584 void QQuickView::mousePressEvent(QMouseEvent *e)
585 {
586     QQmlProfilerService::addEvent(QQmlProfilerService::Mouse);
587
588     QQuickWindow::mousePressEvent(e);
589 }
590
591 /*! \reimp */
592 void QQuickView::mouseReleaseEvent(QMouseEvent *e)
593 {
594     QQmlProfilerService::addEvent(QQmlProfilerService::Mouse);
595
596     QQuickWindow::mouseReleaseEvent(e);
597 }
598
599
600 QT_END_NAMESPACE