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