1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "QtQuick1/qdeclarativeview.h"
44 #include <QtDeclarative/qdeclarative.h>
45 #include <QtQuick1/qdeclarativeitem.h>
46 #include <QtDeclarative/qdeclarativeengine.h>
47 #include <QtDeclarative/qdeclarativecontext.h>
48 #include <QtDeclarative/private/qdeclarativeglobal_p.h>
49 #include <QtDeclarative/private/qdeclarativeguard_p.h>
51 #include <QtDeclarative/private/qdeclarativedebugtrace_p.h>
52 #include <QtDeclarative/private/qdeclarativeinspectorservice_p.h>
58 #include <qcoreapplication.h>
59 #include <qfontdatabase.h>
64 #include <qgraphicswidget.h>
65 #include <qbasictimer.h>
66 #include <QtCore/qabstractanimation.h>
67 #include <QtQuick1/private/qdeclarativeitem_p.h>
68 #include <QtWidgets/private/qgraphicsview_p.h>
69 #include <private/qabstractanimation_p.h>
70 #include <QtQuick1/private/qdeclarativeitemchangelistener_p.h>
74 DEFINE_BOOL_CONFIG_OPTION(frameRateDebug, QML_SHOW_FRAMERATE)
76 class QDeclarative1Scene : public QGraphicsScene
79 QDeclarative1Scene(QObject *parent = 0);
82 virtual void keyPressEvent(QKeyEvent *);
83 virtual void keyReleaseEvent(QKeyEvent *);
85 virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *);
86 virtual void mousePressEvent(QGraphicsSceneMouseEvent *);
87 virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
90 QDeclarative1Scene::QDeclarative1Scene(QObject *parent) : QGraphicsScene(parent)
94 void QDeclarative1Scene::keyPressEvent(QKeyEvent *e)
96 QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Key);
98 QGraphicsScene::keyPressEvent(e);
101 void QDeclarative1Scene::keyReleaseEvent(QKeyEvent *e)
103 QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Key);
105 QGraphicsScene::keyReleaseEvent(e);
108 void QDeclarative1Scene::mouseMoveEvent(QGraphicsSceneMouseEvent *e)
110 QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Mouse);
112 QGraphicsScene::mouseMoveEvent(e);
115 void QDeclarative1Scene::mousePressEvent(QGraphicsSceneMouseEvent *e)
117 QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Mouse);
119 QGraphicsScene::mousePressEvent(e);
122 void QDeclarative1Scene::mouseReleaseEvent(QGraphicsSceneMouseEvent *e)
124 QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Mouse);
126 QGraphicsScene::mouseReleaseEvent(e);
129 class QDeclarativeViewPrivate : public QGraphicsViewPrivate, public QDeclarativeItemChangeListener
131 Q_DECLARE_PUBLIC(QDeclarativeView)
133 QDeclarativeViewPrivate()
134 : root(0), declarativeItemRoot(0), graphicsWidgetRoot(0), component(0),
135 resizeMode(QDeclarativeView::SizeViewToRootObject), initialSize(0,0) {}
136 ~QDeclarativeViewPrivate() { delete root; delete engine; }
138 void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry);
141 inline QSize rootObjectSize() const;
143 QDeclarativeGuard<QGraphicsObject> root;
144 QDeclarativeGuard<QDeclarativeItem> declarativeItemRoot;
145 QDeclarativeGuard<QGraphicsWidget> graphicsWidgetRoot;
149 QDeclarativeEngine* engine;
150 QDeclarativeComponent *component;
151 QBasicTimer resizetimer;
153 QDeclarativeView::ResizeMode resizeMode;
155 QElapsedTimer frameTimer;
160 void QDeclarativeViewPrivate::execute()
162 Q_Q(QDeclarativeView);
171 if (!source.isEmpty()) {
172 component = new QDeclarativeComponent(engine, source, q);
173 if (!component->isLoading()) {
174 q->continueExecute();
176 QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), q, SLOT(continueExecute()));
181 void QDeclarativeViewPrivate::itemGeometryChanged(QDeclarativeItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry)
183 Q_Q(QDeclarativeView);
184 if (resizeItem == root && resizeMode == QDeclarativeView::SizeViewToRootObject) {
185 // wait for both width and height to be changed
186 resizetimer.start(0,q);
188 QDeclarativeItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry);
192 \class QDeclarativeView
194 \brief The QDeclarativeView class provides a widget for displaying a Qt Declarative user interface.
196 QDeclarativeItem objects can be placed on a standard QGraphicsScene and
197 displayed with QGraphicsView. QDeclarativeView is a QGraphicsView subclass
198 provided as a convenience for displaying QML files, and connecting between
199 QML and C++ Qt objects.
201 QDeclarativeView provides:
204 \o Management of QDeclarativeComponent loading and object creation
205 \o Initialization of QGraphicsView for optimal performance with QML using these settings:
207 \o QGraphicsView::setOptimizationFlags(QGraphicsView::DontSavePainterState)
208 \o QGraphicsView::setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate)
209 \o QGraphicsScene::setItemIndexMethod(QGraphicsScene::NoIndex)
211 \o Initialization of QGraphicsView for QML key handling using these settings:
213 \o QGraphicsView::viewport()->setFocusPolicy(Qt::NoFocus)
214 \o QGraphicsView::setFocusPolicy(Qt::StrongFocus)
215 \o QGraphicsScene::setStickyFocus(true)
222 QDeclarativeView *view = new QDeclarativeView;
223 view->setSource(QUrl::fromLocalFile("myqmlfile.qml"));
227 Since QDeclarativeView is a QWidget-based class, it can be used to
228 display QML interfaces within QWidget-based GUI applications that do not
229 use the Graphics View framework.
231 To receive errors related to loading and executing QML with QDeclarativeView,
232 you can connect to the statusChanged() signal and monitor for QDeclarativeView::Error.
233 The errors are available via QDeclarativeView::errors().
235 If you're using your own QGraphicsScene-based scene with QDeclarativeView, remember to
236 enable scene's sticky focus mode and to set itemIndexMethod to QGraphicsScene::NoIndex.
238 \sa {Integrating QML Code with Existing Qt UI Code}, {Using QML Bindings in C++ Applications}
242 /*! \fn void QDeclarativeView::sceneResized(QSize size)
243 This signal is emitted when the view is resized to \a size.
246 /*! \fn void QDeclarativeView::statusChanged(QDeclarativeView::Status status)
247 This signal is emitted when the component's current \a status changes.
250 /*! \fn void QDeclarativeView::initialSizeChanged(QSize size)
255 \fn QDeclarativeView::QDeclarativeView(QWidget *parent)
257 Constructs a QDeclarativeView with the given \a parent.
259 QDeclarativeView::QDeclarativeView(QWidget *parent)
260 : QGraphicsView(*(new QDeclarativeViewPrivate), parent)
262 Q_D(QDeclarativeView);
263 setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred);
268 \fn QDeclarativeView::QDeclarativeView(const QUrl &source, QWidget *parent)
270 Constructs a QDeclarativeView with the given QML \a source and \a parent.
272 QDeclarativeView::QDeclarativeView(const QUrl &source, QWidget *parent)
273 : QGraphicsView(*(new QDeclarativeViewPrivate), parent)
275 Q_D(QDeclarativeView);
276 setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred);
281 void QDeclarativeViewPrivate::init()
283 Q_Q(QDeclarativeView);
284 engine = new QDeclarativeEngine();
285 q->setScene(new QDeclarative1Scene(q));
287 q->setOptimizationFlags(QGraphicsView::DontSavePainterState);
288 q->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
289 q->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
292 // These seem to give the best performance
293 q->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
294 q->scene()->setItemIndexMethod(QGraphicsScene::NoIndex);
295 q->viewport()->setFocusPolicy(Qt::NoFocus);
296 q->setFocusPolicy(Qt::StrongFocus);
298 q->scene()->setStickyFocus(true); //### needed for correct focus handling
300 #ifdef QDECLARATIVEVIEW_NOBACKGROUND
301 q->setAttribute(Qt::WA_OpaquePaintEvent);
302 q->setAttribute(Qt::WA_NoSystemBackground);
303 q->viewport()->setAttribute(Qt::WA_OpaquePaintEvent);
304 q->viewport()->setAttribute(Qt::WA_NoSystemBackground);
307 if (QDeclarativeDebugService::isDebuggingEnabled())
308 QDeclarativeInspectorService::instance()->addView(q);
314 QDeclarativeView::~QDeclarativeView()
316 if (QDeclarativeDebugService::isDebuggingEnabled())
317 QDeclarativeInspectorService::instance()->removeView(this);
320 /*! \property QDeclarativeView::source
321 \brief The URL of the source of the QML component.
323 Changing this property causes the QML component to be reloaded.
325 Ensure that the URL provided is full and correct, in particular, use
326 \l QUrl::fromLocalFile() when loading a file from the local filesystem.
330 Sets the source to the \a url, loads the QML component and instantiates it.
332 Ensure that the URL provided is full and correct, in particular, use
333 \l QUrl::fromLocalFile() when loading a file from the local filesystem.
335 Calling this methods multiple times with the same url will result
336 in the QML being reloaded.
338 void QDeclarativeView::setSource(const QUrl& url)
340 Q_D(QDeclarativeView);
346 Returns the source URL, if set.
350 QUrl QDeclarativeView::source() const
352 Q_D(const QDeclarativeView);
357 Returns a pointer to the QDeclarativeEngine used for instantiating
360 QDeclarativeEngine* QDeclarativeView::engine() const
362 Q_D(const QDeclarativeView);
367 This function returns the root of the context hierarchy. Each QML
368 component is instantiated in a QDeclarativeContext. QDeclarativeContext's are
369 essential for passing data to QML components. In QML, contexts are
370 arranged hierarchically and this hierarchy is managed by the
373 QDeclarativeContext* QDeclarativeView::rootContext() const
375 Q_D(const QDeclarativeView);
376 return d->engine->rootContext();
380 \enum QDeclarativeView::Status
381 Specifies the loading status of the QDeclarativeView.
383 \value Null This QDeclarativeView has no source set.
384 \value Ready This QDeclarativeView has loaded and created the QML component.
385 \value Loading This QDeclarativeView is loading network data.
386 \value Error One or more errors has occurred. Call errors() to retrieve a list
390 /*! \enum QDeclarativeView::ResizeMode
392 This enum specifies how to resize the view.
394 \value SizeViewToRootObject The view resizes with the root item in the QML.
395 \value SizeRootObjectToView The view will automatically resize the root item to the size of the view.
399 \property QDeclarativeView::status
400 The component's current \l{QDeclarativeView::Status} {status}.
403 QDeclarativeView::Status QDeclarativeView::status() const
405 Q_D(const QDeclarativeView);
407 return QDeclarativeView::Null;
409 return QDeclarativeView::Status(d->component->status());
413 Return the list of errors that occurred during the last compile or create
414 operation. When the status is not Error, an empty list is returned.
416 QList<QDeclarativeError> QDeclarativeView::errors() const
418 Q_D(const QDeclarativeView);
420 return d->component->errors();
421 return QList<QDeclarativeError>();
425 \property QDeclarativeView::resizeMode
426 \brief whether the view should resize the canvas contents
428 If this property is set to SizeViewToRootObject (the default), the view
429 resizes with the root item in the QML.
431 If this property is set to SizeRootObjectToView, the view will
432 automatically resize the root item.
434 Regardless of this property, the sizeHint of the view
435 is the initial size of the root item. Note though that
436 since QML may load dynamically, that size may change.
439 void QDeclarativeView::setResizeMode(ResizeMode mode)
441 Q_D(QDeclarativeView);
442 if (d->resizeMode == mode)
445 if (d->declarativeItemRoot) {
446 if (d->resizeMode == SizeViewToRootObject) {
447 QDeclarativeItemPrivate *p =
448 static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(d->declarativeItemRoot));
449 p->removeItemChangeListener(d, QDeclarativeItemPrivate::Geometry);
451 } else if (d->graphicsWidgetRoot) {
452 if (d->resizeMode == QDeclarativeView::SizeViewToRootObject) {
453 d->graphicsWidgetRoot->removeEventFilter(this);
457 d->resizeMode = mode;
463 void QDeclarativeViewPrivate::initResize()
465 Q_Q(QDeclarativeView);
466 if (declarativeItemRoot) {
467 if (resizeMode == QDeclarativeView::SizeViewToRootObject) {
468 QDeclarativeItemPrivate *p =
469 static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(declarativeItemRoot));
470 p->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
472 } else if (graphicsWidgetRoot) {
473 if (resizeMode == QDeclarativeView::SizeViewToRootObject) {
474 graphicsWidgetRoot->installEventFilter(q);
480 void QDeclarativeViewPrivate::updateSize()
482 Q_Q(QDeclarativeView);
485 if (declarativeItemRoot) {
486 if (resizeMode == QDeclarativeView::SizeViewToRootObject) {
487 QSize newSize = QSize(declarativeItemRoot->width(), declarativeItemRoot->height());
488 if (newSize.isValid() && newSize != q->size()) {
491 } else if (resizeMode == QDeclarativeView::SizeRootObjectToView) {
492 if (!qFuzzyCompare(q->width(), declarativeItemRoot->width()))
493 declarativeItemRoot->setWidth(q->width());
494 if (!qFuzzyCompare(q->height(), declarativeItemRoot->height()))
495 declarativeItemRoot->setHeight(q->height());
497 } else if (graphicsWidgetRoot) {
498 if (resizeMode == QDeclarativeView::SizeViewToRootObject) {
499 QSize newSize = QSize(graphicsWidgetRoot->size().width(), graphicsWidgetRoot->size().height());
500 if (newSize.isValid() && newSize != q->size()) {
503 } else if (resizeMode == QDeclarativeView::SizeRootObjectToView) {
504 QSizeF newSize = QSize(q->size().width(), q->size().height());
505 if (newSize.isValid() && newSize != graphicsWidgetRoot->size()) {
506 graphicsWidgetRoot->resize(newSize);
513 QSize QDeclarativeViewPrivate::rootObjectSize() const
515 QSize rootObjectSize(0,0);
516 int widthCandidate = -1;
517 int heightCandidate = -1;
519 QSizeF size = root->boundingRect().size();
520 widthCandidate = size.width();
521 heightCandidate = size.height();
523 if (widthCandidate > 0) {
524 rootObjectSize.setWidth(widthCandidate);
526 if (heightCandidate > 0) {
527 rootObjectSize.setHeight(heightCandidate);
529 return rootObjectSize;
532 QDeclarativeView::ResizeMode QDeclarativeView::resizeMode() const
534 Q_D(const QDeclarativeView);
535 return d->resizeMode;
541 void QDeclarativeView::continueExecute()
543 Q_D(QDeclarativeView);
544 disconnect(d->component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), this, SLOT(continueExecute()));
546 if (d->component->isError()) {
547 QList<QDeclarativeError> errorList = d->component->errors();
548 foreach (const QDeclarativeError &error, errorList) {
551 emit statusChanged(status());
555 QObject *obj = d->component->create();
557 if(d->component->isError()) {
558 QList<QDeclarativeError> errorList = d->component->errors();
559 foreach (const QDeclarativeError &error, errorList) {
562 emit statusChanged(status());
567 emit statusChanged(status());
573 void QDeclarativeView::setRootObject(QObject *obj)
575 Q_D(QDeclarativeView);
576 if (d->root == obj || !scene())
578 if (QDeclarativeItem *declarativeItem = qobject_cast<QDeclarativeItem *>(obj)) {
579 scene()->addItem(declarativeItem);
580 d->root = declarativeItem;
581 d->declarativeItemRoot = declarativeItem;
582 } else if (QGraphicsObject *graphicsObject = qobject_cast<QGraphicsObject *>(obj)) {
583 scene()->addItem(graphicsObject);
584 d->root = graphicsObject;
585 if (graphicsObject->isWidget()) {
586 d->graphicsWidgetRoot = static_cast<QGraphicsWidget*>(graphicsObject);
588 qWarning() << "QDeclarativeView::resizeMode is not honored for components of type QGraphicsObject";
591 qWarning() << "QDeclarativeView only supports loading of root objects that derive from QGraphicsObject";
592 if (QWidget* widget = qobject_cast<QWidget *>(obj)) {
593 window()->setAttribute(Qt::WA_OpaquePaintEvent, false);
594 window()->setAttribute(Qt::WA_NoSystemBackground, false);
595 if (layout() && layout()->count()) {
596 // Hide the QGraphicsView in GV mode.
597 QLayoutItem *item = layout()->itemAt(0);
599 item->widget()->hide();
601 widget->setParent(this);
603 widget->setVisible(true);
605 resize(widget->size());
612 d->initialSize = d->rootObjectSize();
613 if ((d->resizeMode == QDeclarativeView::SizeViewToRootObject || !testAttribute(Qt::WA_Resized))
614 && d->initialSize != size()) {
615 if (!(parentWidget() && parentWidget()->layout())) {
616 resize(d->initialSize);
619 emit initialSizeChanged(d->initialSize);
626 If the \l {QTimerEvent} {timer event} \a e is this
627 view's resize timer, sceneResized() is emitted.
629 void QDeclarativeView::timerEvent(QTimerEvent* e)
631 Q_D(QDeclarativeView);
632 if (!e || e->timerId() == d->resizetimer.timerId()) {
634 d->resizetimer.stop();
639 bool QDeclarativeView::eventFilter(QObject *watched, QEvent *e)
641 Q_D(QDeclarativeView);
642 if (watched == d->root && d->resizeMode == SizeViewToRootObject) {
643 if (d->graphicsWidgetRoot) {
644 if (e->type() == QEvent::GraphicsSceneResize) {
649 return QGraphicsView::eventFilter(watched, e);
654 Preferred size follows the root object geometry.
656 QSize QDeclarativeView::sizeHint() const
658 Q_D(const QDeclarativeView);
659 QSize rootObjectSize = d->rootObjectSize();
660 if (rootObjectSize.isEmpty()) {
663 return rootObjectSize;
668 Returns the initial size of the root object
670 QSize QDeclarativeView::initialSize() const
672 Q_D(const QDeclarativeView);
673 return d->initialSize;
677 Returns the view's root \l {QGraphicsObject} {item}.
679 QGraphicsObject *QDeclarativeView::rootObject() const
681 Q_D(const QDeclarativeView);
687 This function handles the \l {QResizeEvent} {resize event}
690 void QDeclarativeView::resizeEvent(QResizeEvent *e)
692 Q_D(QDeclarativeView);
693 if (d->resizeMode == SizeRootObjectToView) {
696 if (d->declarativeItemRoot) {
697 setSceneRect(QRectF(0, 0, d->declarativeItemRoot->width(), d->declarativeItemRoot->height()));
698 } else if (d->root) {
699 setSceneRect(d->root->boundingRect());
701 setSceneRect(rect());
703 emit sceneResized(e->size());
704 QGraphicsView::resizeEvent(e);
710 void QDeclarativeView::paintEvent(QPaintEvent *event)
712 Q_D(QDeclarativeView);
714 QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::FramePaint);
715 QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Painting);
718 if (frameRateDebug())
719 time = d->frameTimer.restart();
721 QGraphicsView::paintEvent(event);
723 QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Painting);
725 if (frameRateDebug())
726 qDebug() << "paintEvent:" << d->frameTimer.elapsed() << "time since last frame:" << time;
728 #if QT_SHOW_DECLARATIVEVIEW_FPS
734 } else if (timer.elapsed() > 5000) {
735 qreal avgtime = timer.elapsed() / (qreal) frames;
736 qDebug("Average time per frame: %f ms (%i fps)", avgtime, int(1000 / avgtime));
746 QDeclarativeItem * QDeclarativeView::accessibleRootItem() const
748 Q_D(const QDeclarativeView);
749 return d->declarativeItemRoot;