summaryrefslogtreecommitdiffstats
path: root/src/declarative/util/qdeclarativeview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/declarative/util/qdeclarativeview.cpp')
-rw-r--r--src/declarative/util/qdeclarativeview.cpp570
1 files changed, 570 insertions, 0 deletions
diff --git a/src/declarative/util/qdeclarativeview.cpp b/src/declarative/util/qdeclarativeview.cpp
new file mode 100644
index 0000000..6fe5bf3
--- /dev/null
+++ b/src/declarative/util/qdeclarativeview.cpp
@@ -0,0 +1,570 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdeclarativeview.h"
+
+#include "qperformancelog_p_p.h"
+#include "qfxperf_p_p.h"
+
+#include <qdeclarative.h>
+#include <qdeclarativeitem.h>
+#include <qdeclarativeengine.h>
+#include <qdeclarativecontext.h>
+#include <qdeclarativedebug_p.h>
+#include <qdeclarativedebugservice_p.h>
+#include <qdeclarativeglobal_p.h>
+
+#include <qscriptvalueiterator.h>
+#include <qdebug.h>
+#include <qtimer.h>
+#include <qevent.h>
+#include <qdir.h>
+#include <qcoreapplication.h>
+#include <qfontdatabase.h>
+#include <qicon.h>
+#include <qurl.h>
+#include <qboxlayout.h>
+#include <qbasictimer.h>
+#include <QtCore/qabstractanimation.h>
+#include <private/qgraphicsview_p.h>
+
+QT_BEGIN_NAMESPACE
+
+DEFINE_BOOL_CONFIG_OPTION(frameRateDebug, QML_SHOW_FRAMERATE)
+
+class QDeclarativeViewDebugServer;
+class FrameBreakAnimation : public QAbstractAnimation
+{
+public:
+ FrameBreakAnimation(QDeclarativeViewDebugServer *s)
+ : QAbstractAnimation((QObject*)s), server(s)
+ {
+ start();
+ }
+
+ virtual int duration() const { return -1; }
+ virtual void updateCurrentTime(int msecs);
+
+private:
+ QDeclarativeViewDebugServer *server;
+};
+
+class QDeclarativeViewDebugServer : public QDeclarativeDebugService
+{
+public:
+ QDeclarativeViewDebugServer(QObject *parent = 0) : QDeclarativeDebugService(QLatin1String("CanvasFrameRate"), parent), breaks(0)
+ {
+ timer.start();
+ new FrameBreakAnimation(this);
+ }
+
+ void addTiming(int pe, int tbf)
+ {
+ if (!isEnabled())
+ return;
+
+ bool isFrameBreak = breaks > 1;
+ breaks = 0;
+ int e = timer.elapsed();
+ QByteArray data;
+ QDataStream ds(&data, QIODevice::WriteOnly);
+ ds << (int)pe << (int)tbf << (int)e
+ << (bool)isFrameBreak;
+ sendMessage(data);
+ }
+
+ void frameBreak() { ++breaks; }
+
+private:
+ QTime timer;
+ int breaks;
+};
+
+Q_GLOBAL_STATIC(QDeclarativeViewDebugServer, qfxViewDebugServer);
+
+void FrameBreakAnimation::updateCurrentTime(int msecs)
+{
+ Q_UNUSED(msecs);
+ server->frameBreak();
+}
+
+class QDeclarativeViewPrivate
+{
+public:
+ QDeclarativeViewPrivate(QDeclarativeView *view)
+ : q(view), root(0), component(0), resizeMode(QDeclarativeView::SizeViewToRootObject) {}
+
+ void execute();
+
+ QDeclarativeView *q;
+
+ QGuard<QGraphicsObject> root;
+ QGuard<QDeclarativeItem> qmlRoot;
+
+ QUrl source;
+
+ QDeclarativeEngine engine;
+ QDeclarativeComponent *component;
+ QBasicTimer resizetimer;
+
+ mutable QSize initialSize;
+ QDeclarativeView::ResizeMode resizeMode;
+ QTime frameTimer;
+
+ void init();
+
+ QGraphicsScene scene;
+};
+
+void QDeclarativeViewPrivate::execute()
+{
+ delete root;
+ delete component;
+ component = new QDeclarativeComponent(&engine, source, q);
+
+ if (!component->isLoading()) {
+ q->continueExecute();
+ } else {
+ QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), q, SLOT(continueExecute()));
+ }
+}
+
+
+/*!
+ \class QDeclarativeView
+ \since 4.7
+ \brief The QDeclarativeView class provides a widget for displaying a Qt Declarative user interface.
+
+ Any QGraphicsObject or QDeclarativeItem
+ created via QML can be placed on a standard QGraphicsScene and viewed with a standard
+ QGraphicsView.
+
+ QDeclarativeView is a QGraphicsView subclass provided as a convenience for displaying QML
+ files, and connecting between QML and C++ Qt objects.
+
+ QDeclarativeView performs the following functions:
+
+ \list
+ \o Manages QDeclarativeComponent loading and object creation.
+ \o Initializes QGraphicsView for optimal performance with QML:
+ \list
+ \o QGraphicsView::setOptimizationFlags(QGraphicsView::DontSavePainterState);
+ \o QGraphicsView::setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
+ \o QGraphicsScene::setItemIndexMethod(QGraphicsScene::NoIndex);
+ \endlist
+ \o Initializes QGraphicsView for QML key handling:
+ \list
+ \o QGraphicsView::viewport()->setFocusPolicy(Qt::NoFocus);
+ \o QGraphicsScene::setStickyFocus(true);
+ \endlist
+ \endlist
+
+ Typical usage:
+ \code
+ ...
+ QDeclarativeView *view = new QDeclarativeView(this);
+ vbox->addWidget(view);
+
+ QUrl url(fileName);
+ view->setSource(url);
+ view->show();
+ \endcode
+
+ To receive errors related to loading and executing QML with QDeclarativeView,
+ you can connect to the statusChanged() signal and monitor for QDeclarativeView::Error.
+ The errors are available via QDeclarativeView::errors().
+*/
+
+
+/*! \fn void QDeclarativeView::sceneResized(QSize size)
+ This signal is emitted when the view is resized to \a size.
+*/
+
+/*! \fn void QDeclarativeView::statusChanged(QDeclarativeView::Status status)
+ This signal is emitted when the component's current \l{QDeclarativeView::Status} {status} changes.
+*/
+
+/*!
+ \fn QDeclarativeView::QDeclarativeView(QWidget *parent)
+
+ Constructs a QDeclarativeView with the given \a parent.
+*/
+QDeclarativeView::QDeclarativeView(QWidget *parent)
+: QGraphicsView(parent), d(new QDeclarativeViewPrivate(this))
+{
+ setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred);
+ d->init();
+}
+
+/*!
+ \fn QDeclarativeView::QDeclarativeView(const QUrl &source, QWidget *parent)
+
+ Constructs a QDeclarativeView with the given QML \a source and \a parent.
+*/
+QDeclarativeView::QDeclarativeView(const QUrl &source, QWidget *parent)
+: QGraphicsView(parent), d(new QDeclarativeViewPrivate(this))
+{
+ setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred);
+ d->init();
+ setSource(source);
+}
+
+void QDeclarativeViewPrivate::init()
+{
+#ifdef Q_ENABLE_PERFORMANCE_LOG
+ {
+ QDeclarativePerfTimer<QDeclarativePerf::FontDatabase> perf;
+ QFontDatabase database;
+ }
+#endif
+
+ q->setScene(&scene);
+
+ q->setOptimizationFlags(QGraphicsView::DontSavePainterState);
+ q->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ q->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ q->setFrameStyle(0);
+
+ // These seem to give the best performance
+ q->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
+ scene.setItemIndexMethod(QGraphicsScene::NoIndex);
+ q->viewport()->setFocusPolicy(Qt::NoFocus);
+
+ scene.setStickyFocus(true); //### needed for correct focus handling
+}
+
+/*!
+ The destructor clears the view's \l {QGraphicsObject} {items} and
+ deletes the internal representation.
+ */
+QDeclarativeView::~QDeclarativeView()
+{
+ delete d->root;
+}
+
+/*!
+ Sets the source to the \a url, loads the QML component and instantiates it.
+ */
+void QDeclarativeView::setSource(const QUrl& url)
+{
+ if (url != d->source) {
+ d->source = url;
+ d->execute();
+ }
+}
+
+/*!
+ Returns the source URL, if set.
+
+ \sa setSource()
+ */
+QUrl QDeclarativeView::source() const
+{
+ return d->source;
+}
+
+/*!
+ Returns a pointer to the QDeclarativeEngine used for instantiating
+ QML Components.
+ */
+QDeclarativeEngine* QDeclarativeView::engine()
+{
+ return &d->engine;
+}
+
+/*!
+ This function returns the root of the context hierarchy. Each QML
+ component is instantiated in a QDeclarativeContext. QDeclarativeContext's are
+ essential for passing data to QML components. In QML, contexts are
+ arranged hierarchically and this hierarchy is managed by the
+ QDeclarativeEngine.
+ */
+QDeclarativeContext* QDeclarativeView::rootContext()
+{
+ return d->engine.rootContext();
+}
+
+
+/*!
+ \enum QDeclarativeView::Status
+
+ Specifies the loading status of the QDeclarativeView.
+
+ \value Null This QDeclarativeView has no source set.
+ \value Ready This QDeclarativeView has loaded and created the QML component.
+ \value Loading This QDeclarativeView is loading network data.
+ \value Error An error has occured. Calling errorDescription() to retrieve a description.
+*/
+
+/*!
+ \property QDeclarativeView::status
+ The component's current \l{QDeclarativeView::Status} {status}.
+*/
+
+QDeclarativeView::Status QDeclarativeView::status() const
+{
+ if (!d->component)
+ return QDeclarativeView::Null;
+
+ return QDeclarativeView::Status(d->component->status());
+}
+
+/*!
+ Return the list of errors that occured during the last compile or create
+ operation. An empty list is returned if isError() is not set.
+*/
+QList<QDeclarativeError> QDeclarativeView::errors() const
+{
+ if (d->component)
+ return d->component->errors();
+ return QList<QDeclarativeError>();
+}
+
+
+/*!
+ \property QDeclarativeView::resizeMode
+ \brief whether the view should resize the canvas contents
+
+ If this property is set to SizeViewToRootObject (the default), the view
+ resizes with the root item in the QML.
+
+ If this property is set to SizeRootObjectToView, the view will
+ automatically resize the root item.
+
+ Regardless of this property, the sizeHint of the view
+ is the initial size of the root item. Note though that
+ since QML may load dynamically, that size may change.
+
+ \sa initialSize()
+*/
+
+void QDeclarativeView::setResizeMode(ResizeMode mode)
+{
+ if (d->resizeMode == mode)
+ return;
+
+ d->resizeMode = mode;
+ if (d->qmlRoot) {
+ if (d->resizeMode == SizeRootObjectToView) {
+ d->qmlRoot->setWidth(width());
+ d->qmlRoot->setHeight(height());
+ } else {
+ d->qmlRoot->setWidth(d->initialSize.width());
+ d->qmlRoot->setHeight(d->initialSize.height());
+ }
+ }
+}
+
+QDeclarativeView::ResizeMode QDeclarativeView::resizeMode() const
+{
+ return d->resizeMode;
+}
+
+/*!
+ \internal
+ */
+void QDeclarativeView::continueExecute()
+{
+
+ disconnect(d->component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), this, SLOT(continueExecute()));
+
+ if (d->component->isError()) {
+ QList<QDeclarativeError> errorList = d->component->errors();
+ foreach (const QDeclarativeError &error, errorList) {
+ qWarning() << error;
+ }
+ emit statusChanged(status());
+ return;
+ }
+
+ QObject *obj = d->component->create();
+
+ if(d->component->isError()) {
+ QList<QDeclarativeError> errorList = d->component->errors();
+ foreach (const QDeclarativeError &error, errorList) {
+ qWarning() << error;
+ }
+ emit statusChanged(status());
+ return;
+ }
+
+ setRootObject(obj);
+ emit statusChanged(status());
+}
+
+
+/*!
+ \internal
+*/
+void QDeclarativeView::setRootObject(QObject *obj)
+{
+ if (QDeclarativeItem *item = qobject_cast<QDeclarativeItem *>(obj)) {
+ d->scene.addItem(item);
+
+ QPerformanceLog::displayData();
+ QPerformanceLog::clear();
+ d->root = item;
+ d->qmlRoot = item;
+ connect(item, SIGNAL(widthChanged()), this, SLOT(sizeChanged()));
+ connect(item, SIGNAL(heightChanged()), this, SLOT(sizeChanged()));
+ if (d->initialSize.height() <= 0 && d->qmlRoot->width() > 0)
+ d->initialSize.setWidth(d->qmlRoot->width());
+ if (d->initialSize.height() <= 0 && d->qmlRoot->height() > 0)
+ d->initialSize.setHeight(d->qmlRoot->height());
+ resize(d->initialSize);
+
+ if (d->resizeMode == SizeRootObjectToView) {
+ d->qmlRoot->setWidth(width());
+ d->qmlRoot->setHeight(height());
+ } else {
+ QSize sz(d->qmlRoot->width(),d->qmlRoot->height());
+ emit sceneResized(sz);
+ resize(sz);
+ }
+ updateGeometry();
+ } else if (QGraphicsObject *item = qobject_cast<QGraphicsObject *>(obj)) {
+ d->scene.addItem(item);
+ qWarning() << "QDeclarativeView::resizeMode is not honored for components of type QGraphicsObject";
+ } else if (QWidget *wid = qobject_cast<QWidget *>(obj)) {
+ window()->setAttribute(Qt::WA_OpaquePaintEvent, false);
+ window()->setAttribute(Qt::WA_NoSystemBackground, false);
+ if (!layout()) {
+ setLayout(new QVBoxLayout);
+ layout()->setContentsMargins(0, 0, 0, 0);
+ } else if (layout()->count()) {
+ // Hide the QGraphicsView in GV mode.
+ QLayoutItem *item = layout()->itemAt(0);
+ if (item->widget())
+ item->widget()->hide();
+ }
+ layout()->addWidget(wid);
+ emit sceneResized(wid->size());
+ }
+}
+
+/*!
+ \internal
+ */
+void QDeclarativeView::sizeChanged()
+{
+ // delay, so we catch both width and height changing.
+ d->resizetimer.start(0,this);
+}
+
+/*!
+ \internal
+ If the \l {QTimerEvent} {timer event} \a e is this
+ view's resize timer, sceneResized() is emitted.
+ */
+void QDeclarativeView::timerEvent(QTimerEvent* e)
+{
+ if (!e || e->timerId() == d->resizetimer.timerId()) {
+ if (d->qmlRoot) {
+ QSize sz(d->qmlRoot->width(),d->qmlRoot->height());
+ emit sceneResized(sz);
+ //if (!d->resizable)
+ //resize(sz);
+ }
+ d->resizetimer.stop();
+ updateGeometry();
+ }
+}
+
+/*!
+ \internal
+ The size hint is the size of the root item.
+*/
+QSize QDeclarativeView::sizeHint() const
+{
+ if (d->qmlRoot) {
+ if (d->initialSize.width() <= 0)
+ d->initialSize.setWidth(d->qmlRoot->width());
+ if (d->initialSize.height() <= 0)
+ d->initialSize.setHeight(d->qmlRoot->height());
+ }
+ return d->initialSize;
+}
+
+/*!
+ Returns the view's root \l {QGraphicsObject} {item}.
+ */
+QGraphicsObject *QDeclarativeView::rootObject() const
+{
+ return d->root;
+}
+
+/*!
+ \internal
+ This function handles the \l {QResizeEvent} {resize event}
+ \a e.
+ */
+void QDeclarativeView::resizeEvent(QResizeEvent *e)
+{
+ if (d->resizeMode == SizeRootObjectToView && d->qmlRoot) {
+ d->qmlRoot->setWidth(width());
+ d->qmlRoot->setHeight(height());
+ }
+ if (d->qmlRoot) {
+ setSceneRect(QRectF(0, 0, d->qmlRoot->width(), d->qmlRoot->height()));
+ } else if (d->root) {
+ setSceneRect(d->root->boundingRect());
+ } else {
+ setSceneRect(rect());
+ }
+ QGraphicsView::resizeEvent(e);
+}
+
+/*!
+ \internal
+*/
+void QDeclarativeView::paintEvent(QPaintEvent *event)
+{
+ int time = 0;
+ if (frameRateDebug() || QDeclarativeViewDebugServer::isDebuggingEnabled())
+ time = d->frameTimer.restart();
+ QGraphicsView::paintEvent(event);
+ if (QDeclarativeViewDebugServer::isDebuggingEnabled())
+ qfxViewDebugServer()->addTiming(d->frameTimer.elapsed(), time);
+ if (frameRateDebug())
+ qDebug() << "paintEvent:" << d->frameTimer.elapsed() << "time since last frame:" << time;
+}
+
+QT_END_NAMESPACE