/**************************************************************************** ** ** 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 Qt scene graph research project. ** ** $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 "qxgraphicsview.h" #include "qxgraphicsview_p.h" #include #include #include #include #include #ifdef Q_WS_QPA #include #endif #include "utilities.h" static bool qt_scenegraph_continuous_update; #ifndef Q_WS_QPA // ### Multisample should always be on and nonblocking swap should always be off // ### in the final version... static QGLFormat getFormat() { QGLFormat format; format.setSwapInterval(1); const QStringList args = qApp->arguments(); if (!args.contains("--no-multisample")) { format.setSampleBuffers(true); } else if (args.contains("--nonblocking-swap")) { format.setSwapInterval(0); } return format; } #endif QxGraphicsViewPrivate::~QxGraphicsViewPrivate() { delete root; } void QxGraphicsViewPrivate::execute() { if (component) { delete component; component = 0; } if (!source.isEmpty()) { component = new QDeclarativeComponent(&engine, source, q); if (!component->isReady()) { QList errors = component->errors(); foreach (const QDeclarativeError &error, errors) qWarning() << error; return; } QObject *o = component->create(); if (!o) { qWarning("Failed to create component object from: %s", qPrintable(source.toString())); return; } QxItem *go = qobject_cast(o); if (go) { root = go; QxItemPrivate *op = QxItemPrivate::get(root); op->view = q; sg->rootNode()->appendChildNode(&op->transformNode); q->resize(go->width(), go->height()); q->setResizeMode(QxGraphicsView::SizeRootObjectToView); } else { qWarning("Created component is not a QxItem, was %s [%s >- %s], source: %s", qPrintable(o->objectName()), o->metaObject()->className(), o->metaObject()->superClass()->className(), qPrintable(source.toString())); } } } QSize QxGraphicsViewPrivate::rootObjectSize() const { QSize size = root ? root->size().toSize() : QSize(0, 0); if (size.width() < 0) size.setWidth(0); if (size.height() < 0) size.setHeight(0); return size; } QxGraphicsView::QxGraphicsView(QWidget *parent) #ifdef Q_WS_QPA : QWidget(parent) #else : QGLWidget(getFormat(), parent) #endif , d(new QxGraphicsViewPrivate) { extern void qt_scenegraph_register_types(); // in qmlscene_global.cpp qt_scenegraph_register_types(); d->q = this; qt_scenegraph_continuous_update = qApp->arguments().contains("--continuous-update"); d->animationDriver.setWidget(this); // ### Eventually kick out... if (!qApp->arguments().contains("--no-vsync-animations")) d->animationDriver.install(); else printf("Not using VSync Animation Driver\n"); } QxGraphicsView::~QxGraphicsView() { delete d; d = 0; } void QxGraphicsView::maybeUpdate() { if (!d->animationDriver.isRunning()) update(); } QxItem *QxGraphicsView::root() { return d->root; } QxGraphicsView::ResizeMode QxGraphicsView::resizeMode() const { return d->resizeMode; } void QxGraphicsView::setResizeMode(ResizeMode mode) { d->resizeMode = mode; d->updateSize(); } void QxGraphicsViewPrivate::updateSize() { if (root) { if (resizeMode == QxGraphicsView::SizeViewToRootObject) { QSize newSize = QSize(root->width(), root->height()); if (newSize.isValid() && newSize != q->size()) { q->resize(newSize); } } else if (resizeMode == QxGraphicsView::SizeRootObjectToView) { if (!qFuzzyCompare(q->width(), root->width())) root->setWidth(q->width()); if (!qFuzzyCompare(q->height(), root->height())) root->setHeight(q->height()); } q->updateGeometry(); } } void QxGraphicsView::paintEvent(QPaintEvent *e) { Q_UNUSED(e) if (!d->sg) { qWarning("QxGraphicsView::paintEvent: painting without a scene graph..."); return; } #ifdef Q_WS_QPA QPlatformGLContext *ctx = window()->platformWindow()->glContext(); #else QGLContext *ctx = const_cast(context()); #endif ctx->makeCurrent(); QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::FramePaint); QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Painting); emit frameStarted(); d->sg->renderer()->setDeviceRect(rect()); d->sg->renderer()->setProjectMatrixToDeviceRect(); d->sg->renderer()->renderScene(); #ifndef Q_WS_QPA // printf("QxGraphicsView: Swapping...\n"); ctx->swapBuffers(); #endif QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Painting); emit frameEnded(); } void QxGraphicsView::resizeEvent(QResizeEvent *e) { if (d->resizeMode == SizeRootObjectToView) { d->updateSize(); } #ifdef Q_WS_QPA QWidget::resizeEvent(e); #else QGLWidget::resizeEvent(e); #endif } QGraphicsSceneMouseEvent *QxGraphicsViewPrivate::sceneMouseEvent(QMouseEvent *e, const QPointF &local) { QEvent::Type type; switch(e->type()) { case QEvent::MouseButtonPress: type = QEvent::GraphicsSceneMousePress; break; case QEvent::MouseButtonRelease: type = QEvent::GraphicsSceneMouseRelease; break; case QEvent::MouseMove: type = QEvent::GraphicsSceneMouseMove; break; case QEvent::MouseButtonDblClick: type = QEvent::GraphicsSceneMouseDoubleClick; break; default: return 0; } QGraphicsSceneMouseEvent *g = new QGraphicsSceneMouseEvent(type); g->setPos(local); g->setScenePos(e->posF()); g->setScreenPos(e->globalPos()); g->setButtons(e->buttons()); g->setButton(e->button()); g->setModifiers(e->modifiers()); return g; } bool QxGraphicsViewPrivate::deliverInitialMouseEvent(QxItem *o, QMouseEvent *e) { QxItemPrivate *op = QxItemPrivate::get(o); if (op->opacity == 0. || !op->visible || op->scale == 0.) return false; if (op->clip) { bool ok = false; QTransform t = op->mapFromGlobalTransform(&ok); if (!ok) return false; QPointF local = t.map(e->pos()); QRectF bounds(qreal(0) ,qreal(0) , o->width(), o->height()); if (!bounds.contains(local)) return false; } bool filter = op->filtersChildMouse; if (filter) mouseFilters.append(o); QxItem *children = op->firstChild; while (children) { if (deliverInitialMouseEvent(children, e)) return true; children = QxItemPrivate::get(children)->nextSibling; } if (filter) mouseFilters.removeLast(); if (op->acceptedButtons & e->button()) { bool ok = false; QTransform t = op->mapFromGlobalTransform(&ok); if (!ok) return false; QPointF local = t.map(e->pos()); QRectF bounds(qreal(0) ,qreal(0) , o->width(), o->height()); if (bounds.contains(local)) { QGraphicsSceneMouseEvent *m = sceneMouseEvent(e, local); mouseGrabber = o; for (int ii = mouseFilters.count() - 1; ii >= 0; --ii) { if (mouseFilters.at(ii)->sceneEventFilter(o, m)) { delete m; return false; } } o->mousePressEvent(m); if (m->isAccepted()) { delete m; return true; } else { mouseGrabber = 0; delete m; return false; } } } return false; } void QxGraphicsView::mousePressEvent(QMouseEvent *e) { if (d->deliverInitialMouseEvent(d->root, e)) { e->accept(); } else { #ifdef Q_WS_QPA QWidget::mousePressEvent(e); #else QGLWidget::mousePressEvent(e); #endif } } void QxGraphicsView::mouseMoveEvent(QMouseEvent *e) { if (d->mouseGrabber) { QTransform t = QxItemPrivate::get(d->mouseGrabber)->mapFromGlobalTransform(); QPointF local = t.map(e->pos()); QGraphicsSceneMouseEvent *m = d->sceneMouseEvent(e, local); for (int ii = d->mouseFilters.count() - 1; ii >= 0; --ii) { if (d->mouseFilters.at(ii)->sceneEventFilter(d->mouseGrabber, m)) { delete m; return; } } d->mouseGrabber->mouseMoveEvent(m); bool wasAccepted = m->isAccepted(); delete m; if (wasAccepted) { e->accept(); return; } } #ifdef Q_WS_QPA QWidget::mouseMoveEvent(e); #else QGLWidget::mouseMoveEvent(e); #endif } void QxGraphicsView::mouseReleaseEvent(QMouseEvent *e) { if (d->mouseGrabber) { QTransform t = QxItemPrivate::get(d->mouseGrabber)->mapFromGlobalTransform(); QPointF local = t.map(e->pos()); QGraphicsSceneMouseEvent *m = d->sceneMouseEvent(e, local); for (int ii = d->mouseFilters.count() - 1; ii >= 0; --ii) { if (d->mouseFilters.at(ii)->sceneEventFilter(d->mouseGrabber, m)) { delete m; d->mouseFilters.clear(); d->mouseGrabber = 0; return; } } d->mouseFilters.clear(); d->mouseGrabber->mouseReleaseEvent(m); d->mouseGrabber = 0; bool wasAccepted = m->isAccepted(); delete m; if (wasAccepted) { e->accept(); return; } } #ifdef Q_WS_QPA QWidget::mouseReleaseEvent(e); #else QGLWidget::mouseReleaseEvent(e); #endif } void QxGraphicsView::keyPressEvent(QKeyEvent *e) { QxItem *item = d->focusItem; if (item) { QxItem *p = item; do { // Accept the event by default e->accept(); // Send it; QxItem::keyPressEvent ignores it. If the event // is filtered out, stop propagating it. //if (p->isBlockedByModalPanel()) // break; QCoreApplication::sendEvent(p, e); } while (!e->isAccepted() /*&& !p->isPanel()*/ && (p = p->parentItem())); } else { e->ignore(); } #ifdef Q_WS_QPA QWidget::keyPressEvent(e); #else QGLWidget::keyPressEvent(e); #endif } void QxGraphicsView::keyReleaseEvent(QKeyEvent *e) { QxItem *item = d->focusItem; if (item) { QxItem *p = item; do { // Accept the event by default e->accept(); // Send it; QxItem::keyReleaseEvent ignores it. If the event // is filtered out, stop propagating it. //if (p->isBlockedByModalPanel()) // break; QCoreApplication::sendEvent(p, e); } while (!e->isAccepted() /*&& !p->isPanel()*/ && (p = p->parentItem())); } else { e->ignore(); } #ifdef Q_WS_QPA QWidget::keyReleaseEvent(e); #else QGLWidget::keyReleaseEvent(e); #endif } void QxGraphicsViewPrivate::setFocusItem(QxItem *item) { //### should never enter this function if we aren't in an active focus chain //XXX finish implementation if (focusItem == item) return; //remove focus from previous focus item if (focusItem) { QxItem *prevFocusItem = focusItem; focusItem = 0; QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason); QCoreApplication::sendEvent(prevFocusItem, &event); } QxItem *fi = item; while (fi && fi->d_func()->isFocusScope) { fi->d_func()->inActiveFocusChain = true; if (fi == fi->d_func()->focusItem) break; if (fi->d_func()->focusItem) fi = fi->d_func()->focusItem; } focusItem = fi; //give focus to new focus item if (focusItem) { focusItem->d_func()->inActiveFocusChain = true; QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason); QCoreApplication::sendEvent(focusItem, &event); } } QxItem *QxGraphicsView::mouseGrabberItem() const { return d->mouseGrabber; } QDeclarativeEngine *QxGraphicsView::engine() const { return &d->engine; } QDeclarativeContext *QxGraphicsView::rootContext() const { return d->engine.rootContext(); } QUrl QxGraphicsView::source() const { return d->source; } void QxGraphicsView::setSource(const QUrl &url) { d->source = url; if (d->sg && d->sg->isReady()) d->execute(); } QSize QxGraphicsView::sizeHint() const { return d->rootObjectSize(); } /*! Returns the scene graph context used by this view. Unless explicitely set, there won't be a context available until after the view has constructed its own GL context. Depending on the platform, this can happen in the constructor or just before the first call to paintEvent(). */ QSGContext *QxGraphicsView::sceneGraphContext() const { return d->sg; } /*! Sets the scene graph context to be used for this view. The scene graph context must not be initialized. */ void QxGraphicsView::setSceneGraphContext(QSGContext *context) { if (context->isReady()) { qWarning("QxGraphicsView::setSGContext: context is already initialized"); return; } if (d->sg) { qWarning("QxGraphicsView::setSGContext: view already has a scene graph context"); return; } d->sg = context; } void QxGraphicsView::showEvent(QShowEvent *e) { #ifdef Q_WS_QPA QWidget::showEvent(e); #else QGLWidget::showEvent(e); #endif initializeSceneGraph(); } /*! Called when there is a GL context ready to initialize the scene graph context and bind together the various things. Right now, this function is responsible for calling execute, as we don't have the proper hooks in the QxItems to respond to the "scene graph is ready, start buidling nodes" signal. */ void QxGraphicsView::initializeSceneGraph() { #ifdef Q_WS_QPA QPlatformWindow *platformWindow = window()->platformWindow(); QPlatformGLContext *platformContext = const_cast(platformWindow->glContext()); if (!platformContext) { qFatal("QxGraphicsView::initializeSceneGraph: No platform GL context"); } platformContext->makeCurrent(); #endif // If the user didn't override with a custom context, use the default one. if (!d->sg) d->sg = new QSGContext(); QGLContext *ctx = const_cast(QGLContext::currentContext()); d->sg->initialize(ctx); d->sg->renderer()->setDeviceRect(rect()); d->sg->renderer()->setProjectMatrixToDeviceRect(); connect(d->sg->renderer(), SIGNAL(sceneGraphChanged()), this, SLOT(maybeUpdate())); // If we have a source to load, but didn't yet load it... if (!d->source.isEmpty() && !d->component) d->execute(); }