1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qquickcanvas.h"
43 #include "qquickcanvas_p.h"
45 #include "qquickitem.h"
46 #include "qquickitem_p.h"
47 #include "qquickevents_p_p.h"
49 #include <QtQuick/private/qsgrenderer_p.h>
50 #include <QtQuick/private/qsgtexture_p.h>
51 #include <QtQuick/private/qsgflashnode_p.h>
52 #include <QtQuick/qsgengine.h>
54 #include <private/qquickwindowmanager_p.h>
56 #include <private/qguiapplication_p.h>
57 #include <QtGui/QInputMethod>
58 #include <QtGui/QCursor>
60 #include <private/qabstractanimation_p.h>
62 #include <QtGui/qpainter.h>
63 #include <QtGui/qevent.h>
64 #include <QtGui/qmatrix4x4.h>
65 #include <QtGui/qstylehints.h>
66 #include <QtCore/qvarlengtharray.h>
67 #include <QtCore/qabstractanimation.h>
68 #include <QtQml/qqmlincubator.h>
70 #include <QtQuick/private/qquickpixmapcache_p.h>
72 #include <private/qqmlprofilerservice_p.h>
76 DEFINE_BOOL_CONFIG_OPTION(qmlTranslateTouchToMouse, QML_TRANSLATE_TOUCH_TO_MOUSE)
78 void QQuickCanvasPrivate::updateFocusItemTransform()
81 QQuickItem *focus = q->activeFocusItem();
82 if (focus && qApp->focusObject() == focus)
83 qApp->inputMethod()->setInputItemTransform(QQuickItemPrivate::get(focus)->itemToCanvasTransform());
86 class QQuickCanvasIncubationController : public QObject, public QQmlIncubationController
89 QQuickCanvasIncubationController(QQuickCanvasPrivate *canvas)
90 : m_canvas(canvas), m_eventSent(false) {}
93 virtual bool event(QEvent *e)
95 if (e->type() == QEvent::User) {
96 Q_ASSERT(m_eventSent);
97 volatile bool *amtp = m_canvas->windowManager->allowMainThreadProcessing();
98 while (incubatingObjectCount()) {
100 incubateWhile(amtp, 2);
103 QCoreApplication::processEvents();
108 return QObject::event(e);
111 virtual void incubatingObjectCountChanged(int count)
113 if (count && !m_eventSent) {
115 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
117 // If no animations are running, the renderer may be waiting
118 m_canvas->windowManager->wakeup();
122 QQuickCanvasPrivate *m_canvas;
126 QAccessibleInterface *QQuickCanvas::accessibleRoot() const
128 return QAccessible::queryAccessibleInterface(const_cast<QQuickCanvas*>(this));
136 Prior to being added to a valid canvas items can set and clear focus with no
137 effect. Only once items are added to a canvas (by way of having a parent set that
138 already belongs to a canvas) do the focus rules apply. Focus goes back to
139 having no effect if an item is removed from a canvas.
141 When an item is moved into a new focus scope (either being added to a canvas
142 for the first time, or having its parent changed), if the focus scope already has
143 a scope focused item that takes precedence over the item being added. Otherwise,
144 the focus of the added tree is used. In the case of of a tree of items being
145 added to a canvas for the first time, which may have a conflicted focus state (two
146 or more items in one scope having focus set), the same rule is applied item by item -
147 thus the first item that has focus will get it (assuming the scope doesn't already
148 have a scope focused item), and the other items will have their focus cleared.
152 // #define FOCUS_DEBUG
153 // #define MOUSE_DEBUG
154 // #define TOUCH_DEBUG
155 // #define DIRTY_DEBUG
157 QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData()
162 QQuickRootItem::QQuickRootItem()
167 void QQuickCanvas::exposeEvent(QExposeEvent *)
170 d->windowManager->exposureChanged(this);
174 void QQuickCanvas::resizeEvent(QResizeEvent *)
177 d->windowManager->resize(this, size());
181 void QQuickCanvas::showEvent(QShowEvent *)
183 d_func()->windowManager->show(this);
187 void QQuickCanvas::hideEvent(QHideEvent *)
189 d_func()->windowManager->hide(this);
193 void QQuickCanvas::focusOutEvent(QFocusEvent *)
196 d->rootItem->setFocus(false);
200 void QQuickCanvas::focusInEvent(QFocusEvent *)
203 d->rootItem->setFocus(true);
204 d->updateFocusItemTransform();
208 void QQuickCanvasPrivate::polishItems()
210 QSet<QQuickItem *> itms = itemsToPolish;
211 itemsToPolish.clear();
213 for (QSet<QQuickItem *>::iterator it = itms.begin(); it != itms.end(); ++it) {
214 QQuickItem *item = *it;
215 QQuickItemPrivate::get(item)->polishScheduled = false;
216 item->updatePolish();
218 updateFocusItemTransform();
222 * This parameter enables that this canvas can be rendered without
223 * being shown on screen. This feature is very limited in what it supports.
225 * There needs to be another window actually showing that we can make current
226 * to get a surface to make current AND for this feature to be useful
227 * one needs to hook into beforeRender() and set the render tareget.
230 void QQuickCanvasPrivate::setRenderWithoutShowing(bool render)
232 if (render == renderWithoutShowing)
236 renderWithoutShowing = render;
239 windowManager->show(q);
241 windowManager->hide(q);
245 void forceUpdate(QQuickItem *item)
247 if (item->flags() & QQuickItem::ItemHasContents)
249 QQuickItemPrivate::get(item)->dirty(QQuickItemPrivate::ChildrenUpdateMask);
251 QList <QQuickItem *> items = item->childItems();
252 for (int i=0; i<items.size(); ++i)
253 forceUpdate(items.at(i));
256 void QQuickCanvasPrivate::syncSceneGraph()
259 forceUpdate(rootItem);
261 QSGRootNode *rootNode = new QSGRootNode;
262 rootNode->appendChildNode(QQuickItemPrivate::get(rootItem)->itemNode());
263 renderer = context->createRenderer();
264 renderer->setRootNode(rootNode);
269 // Copy the current state of clearing from canvas into renderer.
270 renderer->setClearColor(clearColor);
271 QSGRenderer::ClearMode mode = QSGRenderer::ClearStencilBuffer | QSGRenderer::ClearDepthBuffer;
272 if (clearBeforeRendering)
273 mode |= QSGRenderer::ClearColorBuffer;
274 renderer->setClearMode(mode);
278 void QQuickCanvasPrivate::renderSceneGraph(const QSize &size)
281 emit q->beforeRendering();
283 renderer->setDeviceRect(QRect(QPoint(0, 0), size));
284 if (renderTargetId) {
285 fboId = renderTargetId;
286 renderer->setViewportRect(QRect(QPoint(0, 0), renderTargetSize));
288 renderer->setViewportRect(QRect(QPoint(0, 0), size));
290 renderer->setProjectionMatrixToDeviceRect();
292 context->renderNextFrame(renderer, fboId);
293 emit q->afterRendering();
296 QQuickCanvasPrivate::QQuickCanvasPrivate()
299 , mouseGrabberItem(0)
301 , touchMousePressTimestamp(0)
302 , renderWithoutShowing(false)
307 , clearColor(Qt::white)
308 , clearBeforeRendering(true)
309 , persistentGLContext(false)
310 , persistentSceneGraph(false)
313 , incubationController(0)
317 QQuickCanvasPrivate::~QQuickCanvasPrivate()
321 void QQuickCanvasPrivate::init(QQuickCanvas *c)
327 rootItem = new QQuickRootItem;
328 QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(rootItem);
329 rootItemPrivate->canvas = q;
330 rootItemPrivate->canvasRefCount = 1;
331 rootItemPrivate->flags |= QQuickItem::ItemIsFocusScope;
333 // In the absence of a focus in event on some platforms assume the window will
334 // be activated immediately and set focus on the rootItem
335 // ### Remove when QTBUG-22415 is resolved.
336 //It is important that this call happens after the rootItem has a canvas..
337 rootItem->setFocus(true);
339 windowManager = QQuickWindowManager::instance();
340 context = windowManager->sceneGraphContext();
341 q->setSurfaceType(QWindow::OpenGLSurface);
342 q->setFormat(context->defaultSurfaceFormat());
344 QObject::connect(context, SIGNAL(initialized()), q, SIGNAL(sceneGraphInitialized()), Qt::DirectConnection);
345 QObject::connect(context, SIGNAL(invalidated()), q, SIGNAL(sceneGraphInvalidated()), Qt::DirectConnection);
346 QObject::connect(context, SIGNAL(invalidated()), q, SLOT(cleanupSceneGraph()), Qt::DirectConnection);
348 // ### TODO: remove QSGEngine
349 engine = new QSGEngine();
350 engine->setCanvas(q);
353 QQmlListProperty<QObject> QQuickCanvasPrivate::data()
356 return QQuickItemPrivate::get(rootItem)->data();
359 void QQuickCanvasPrivate::initRootItem()
362 q->connect(q, SIGNAL(widthChanged(int)),
363 rootItem, SLOT(setWidth(int)));
364 q->connect(q, SIGNAL(heightChanged(int)),
365 rootItem, SLOT(setHeight(int)));
366 rootItem->setWidth(q->width());
367 rootItem->setHeight(q->height());
370 static QQuickMouseEventEx touchToMouseEvent(QEvent::Type type, const QTouchEvent::TouchPoint &p)
372 QQuickMouseEventEx me(type, p.pos(), p.scenePos(), p.screenPos(),
373 Qt::LeftButton, Qt::LeftButton, 0);
374 me.setVelocity(p.velocity());
378 void QQuickCanvasPrivate::translateTouchToMouse(QTouchEvent *event)
380 if (event->type() == QEvent::TouchCancel) {
382 if (mouseGrabberItem)
383 mouseGrabberItem->ungrabMouse();
386 for (int i = 0; i < event->touchPoints().count(); ++i) {
387 QTouchEvent::TouchPoint p = event->touchPoints().at(i);
388 if (touchMouseId == -1 && p.state() & Qt::TouchPointPressed) {
389 bool doubleClick = event->timestamp() - touchMousePressTimestamp
390 < static_cast<ulong>(qApp->styleHints()->mouseDoubleClickInterval());
391 touchMousePressTimestamp = event->timestamp();
392 QQuickMouseEventEx me = touchToMouseEvent(QEvent::MouseButtonPress, p);
393 me.setTimestamp(event->timestamp());
394 me.setAccepted(false);
395 me.setCapabilities(event->device()->capabilities());
396 deliverMouseEvent(&me);
397 if (me.isAccepted()) {
398 touchMouseId = p.id();
399 event->setAccepted(true);
402 touchMousePressTimestamp = 0;
403 QQuickMouseEventEx me = touchToMouseEvent(QEvent::MouseButtonDblClick, p);
404 me.setTimestamp(event->timestamp());
405 me.setAccepted(false);
406 me.setCapabilities(event->device()->capabilities());
407 if (!mouseGrabberItem) {
408 if (deliverInitialMousePressEvent(rootItem, &me)) {
409 touchMouseId = p.id();
410 event->setAccepted(true);
413 deliverMouseEvent(&me);
414 if (me.isAccepted()) {
415 touchMouseId = p.id();
416 event->setAccepted(true);
420 if (touchMouseId != -1)
422 } else if (p.id() == touchMouseId) {
423 if (p.state() & Qt::TouchPointMoved) {
424 QQuickMouseEventEx me = touchToMouseEvent(QEvent::MouseMove, p);
425 me.setTimestamp(event->timestamp());
426 me.setCapabilities(event->device()->capabilities());
427 if (!mouseGrabberItem) {
428 if (lastMousePosition.isNull())
429 lastMousePosition = me.windowPos();
430 QPointF last = lastMousePosition;
431 lastMousePosition = me.windowPos();
433 bool accepted = me.isAccepted();
434 bool delivered = deliverHoverEvent(rootItem, me.windowPos(), last, me.modifiers(), accepted);
436 //take care of any exits
437 accepted = clearHover();
439 me.setAccepted(accepted);
443 deliverMouseEvent(&me);
444 } else if (p.state() & Qt::TouchPointReleased) {
446 if (!mouseGrabberItem)
448 QQuickMouseEventEx me = touchToMouseEvent(QEvent::MouseButtonRelease, p);
449 me.setTimestamp(event->timestamp());
450 me.setCapabilities(event->device()->capabilities());
451 deliverMouseEvent(&me);
452 if (mouseGrabberItem)
453 mouseGrabberItem->ungrabMouse();
460 void QQuickCanvasPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform)
462 for (int i=0; i<touchPoints.count(); i++) {
463 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
464 touchPoint.setRect(transform.mapRect(touchPoint.sceneRect()));
465 touchPoint.setStartPos(transform.map(touchPoint.startScenePos()));
466 touchPoint.setLastPos(transform.map(touchPoint.lastScenePos()));
472 Translates the data in \a touchEvent to this canvas. This method leaves the item local positions in
473 \a touchEvent untouched (these are filled in later).
475 void QQuickCanvasPrivate::translateTouchEvent(QTouchEvent *touchEvent)
477 // Q_Q(QQuickCanvas);
479 // touchEvent->setWidget(q); // ### refactor...
481 QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
482 for (int i = 0; i < touchPoints.count(); ++i) {
483 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
485 touchPoint.setScreenRect(touchPoint.sceneRect());
486 touchPoint.setStartScreenPos(touchPoint.startScenePos());
487 touchPoint.setLastScreenPos(touchPoint.lastScenePos());
489 touchPoint.setSceneRect(touchPoint.rect());
490 touchPoint.setStartScenePos(touchPoint.startPos());
491 touchPoint.setLastScenePos(touchPoint.lastPos());
494 lastMousePosition = touchPoint.pos().toPoint();
496 touchEvent->setTouchPoints(touchPoints);
499 void QQuickCanvasPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
504 Q_ASSERT(scope || item == rootItem);
507 qWarning() << "QQuickCanvasPrivate::setFocusInScope():";
508 qWarning() << " scope:" << (QObject *)scope;
510 qWarning() << " scopeSubFocusItem:" << (QObject *)QQuickItemPrivate::get(scope)->subFocusItem;
511 qWarning() << " item:" << (QObject *)item;
512 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
515 QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0;
516 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
518 QQuickItem *oldActiveFocusItem = 0;
519 QQuickItem *newActiveFocusItem = 0;
521 QVarLengthArray<QQuickItem *, 20> changed;
523 // Does this change the active focus?
524 if (item == rootItem || (scopePrivate->activeFocus && item->isEnabled())) {
525 oldActiveFocusItem = activeFocusItem;
526 newActiveFocusItem = item;
527 while (newActiveFocusItem->isFocusScope()
528 && newActiveFocusItem->scopedFocusItem()
529 && newActiveFocusItem->scopedFocusItem()->isEnabled()) {
530 newActiveFocusItem = newActiveFocusItem->scopedFocusItem();
533 if (oldActiveFocusItem) {
535 qApp->inputMethod()->commit();
539 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
540 q->sendEvent(oldActiveFocusItem, &event);
542 QQuickItem *afi = oldActiveFocusItem;
543 while (afi && afi != scope) {
544 if (QQuickItemPrivate::get(afi)->activeFocus) {
545 QQuickItemPrivate::get(afi)->activeFocus = false;
548 afi = afi->parentItem();
553 if (item != rootItem && !(options & DontChangeSubFocusItem)) {
554 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
555 if (oldSubFocusItem) {
556 QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
557 changed << oldSubFocusItem;
560 QQuickItemPrivate::get(item)->updateSubFocusItem(scope, true);
563 if (!(options & DontChangeFocusProperty)) {
564 // if (item != rootItem || QGuiApplication::focusWindow() == q) { // QTBUG-22415
565 itemPrivate->focus = true;
570 if (newActiveFocusItem && rootItem->hasFocus()) {
571 activeFocusItem = newActiveFocusItem;
573 QQuickItemPrivate::get(newActiveFocusItem)->activeFocus = true;
574 changed << newActiveFocusItem;
576 QQuickItem *afi = newActiveFocusItem->parentItem();
577 while (afi && afi != scope) {
578 if (afi->isFocusScope()) {
579 QQuickItemPrivate::get(afi)->activeFocus = true;
582 afi = afi->parentItem();
585 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
586 q->sendEvent(newActiveFocusItem, &event);
589 emit q->focusObjectChanged(activeFocusItem);
591 if (!changed.isEmpty())
592 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
595 void QQuickCanvasPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
600 Q_ASSERT(scope || item == rootItem);
603 qWarning() << "QQuickCanvasPrivate::clearFocusInScope():";
604 qWarning() << " scope:" << (QObject *)scope;
605 qWarning() << " item:" << (QObject *)item;
606 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
609 QQuickItemPrivate *scopePrivate = 0;
611 scopePrivate = QQuickItemPrivate::get(scope);
612 if ( !scopePrivate->subFocusItem )
613 return;//No focus, nothing to do.
616 QQuickItem *oldActiveFocusItem = 0;
617 QQuickItem *newActiveFocusItem = 0;
619 QVarLengthArray<QQuickItem *, 20> changed;
621 Q_ASSERT(item == rootItem || item == scopePrivate->subFocusItem);
623 // Does this change the active focus?
624 if (item == rootItem || scopePrivate->activeFocus) {
625 oldActiveFocusItem = activeFocusItem;
626 newActiveFocusItem = scope;
628 Q_ASSERT(oldActiveFocusItem);
631 qApp->inputMethod()->commit();
635 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
636 q->sendEvent(oldActiveFocusItem, &event);
638 QQuickItem *afi = oldActiveFocusItem;
639 while (afi && afi != scope) {
640 if (QQuickItemPrivate::get(afi)->activeFocus) {
641 QQuickItemPrivate::get(afi)->activeFocus = false;
644 afi = afi->parentItem();
648 if (item != rootItem && !(options & DontChangeSubFocusItem)) {
649 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
650 if (oldSubFocusItem && !(options & DontChangeFocusProperty)) {
651 QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
652 changed << oldSubFocusItem;
655 QQuickItemPrivate::get(item)->updateSubFocusItem(scope, false);
657 } else if (!(options & DontChangeFocusProperty)) {
658 QQuickItemPrivate::get(item)->focus = false;
662 if (newActiveFocusItem) {
663 Q_ASSERT(newActiveFocusItem == scope);
664 activeFocusItem = scope;
666 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
667 q->sendEvent(newActiveFocusItem, &event);
670 emit q->focusObjectChanged(activeFocusItem);
672 if (!changed.isEmpty())
673 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
676 void QQuickCanvasPrivate::notifyFocusChangesRecur(QQuickItem **items, int remaining)
678 QQmlGuard<QQuickItem> item(*items);
681 notifyFocusChangesRecur(items + 1, remaining - 1);
684 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
686 if (itemPrivate->notifiedFocus != itemPrivate->focus) {
687 itemPrivate->notifiedFocus = itemPrivate->focus;
688 emit item->focusChanged(itemPrivate->focus);
691 if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) {
692 itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus;
693 itemPrivate->itemChange(QQuickItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus);
694 emit item->activeFocusChanged(itemPrivate->activeFocus);
699 void QQuickCanvasPrivate::dirtyItem(QQuickItem *)
705 void QQuickCanvasPrivate::cleanup(QSGNode *n)
709 Q_ASSERT(!cleanupNodeList.contains(n));
710 cleanupNodeList.append(n);
716 \qmlclass Window QQuickCanvas
717 \inqmlmodule QtQuick.Window 2
718 \brief The Window object creates a new top-level window.
720 The Window object creates a new top-level window for a QtQuick scene. It automatically sets up the
721 window for use with QtQuick 2.0 graphical elements.
726 \brief The QQuickCanvas class provides the canvas for displaying a graphical QML scene
728 QQuickCanvas provides the graphical scene management needed to interact with and display
729 a scene of QQuickItems.
731 A QQuickCanvas always has a single invisible root item. To add items to this canvas,
732 reparent the items to the root item or to an existing item in the scene.
734 For easily displaying a scene from a QML file, see \l{QQuickView}.
737 \section1 Scene Graph and Rendering
739 The QQuickCanvas uses a scene graph on top of OpenGL to render. This scene graph is disconnected
740 from the QML scene and potentially lives in another thread, depending on the platform
741 implementation. Since the rendering scene graph lives independently from the QML scene, it can
742 also be completely released without affecting the state of the QML scene.
744 The sceneGraphInitialized() signal is emitted on the rendering thread before the QML scene is
745 rendered to the screen for the first time. If the rendering scene graph has been released
746 the signal will be emitted again before the next frame is rendered.
748 Rendering is done by first copying the QML scene's state into the rendering scene graph. This is
749 done by calling QQuickItem::updatePaintNode() functions on all items that have changed. This phase
750 is run on the rendering thread with the GUI thread blocked, when a separate rendering thread
751 is being used. The scene can then be rendered.
753 Before the scene graph is rendered, the beforeRendering() signal is emitted. The OpenGL context
754 is bound at this point and the application is free to do its own rendering. Also
755 make sure to disable the clearing of the color buffer, using setClearBeforeRendering(). The
756 default clear color is white and can be changed with setClearColor(). After the scene has
757 been rendered, the afterRendering() signal is emitted. The application can use this to render
758 OpenGL on top of a QML application. Once the frame is fully done and has been swapped,
759 the frameSwapped() signal is emitted.
761 While the scene graph is being rendered on the rendering thread, the GUI will process animations
762 for the next frame. This means that as long as users are not using scene graph API
763 directly, the added complexity of a rendering thread can be completely ignored.
765 When a QQuickCanvas is programatically hidden with hide() or setVisible(false), it will
766 stop rendering and its scene graph and OpenGL context might be released. The
767 sceneGraphInvalidated() signal will be emitted when this happens.
769 \warning It is crucial that OpenGL operations and interaction with the scene graph happens
770 exclusively on the rendering thread, primarily during the updatePaintNode() phase.
772 \warning As signals related to rendering might be emitted from the rendering thread,
773 connections should be made using Qt::DirectConnection
776 \section1 Resource Management
778 QML will typically try to cache images, scene graph nodes, etc to improve performance, but in
779 some low-memory scenarios it might be required to aggressively release these resources. The
780 releaseResources() can be used to force clean up of certain resources. Calling releaseResources()
781 may result in the entire scene graph and its OpenGL context being deleted. The
782 sceneGraphInvalidated() signal will be emitted when this happens.
785 QQuickCanvas::QQuickCanvas(QWindow *parent)
786 : QWindow(*(new QQuickCanvasPrivate), parent)
792 QQuickCanvas::QQuickCanvas(QQuickCanvasPrivate &dd, QWindow *parent)
793 : QWindow(dd, parent)
799 QQuickCanvas::~QQuickCanvas()
803 d->windowManager->canvasDestroyed(this);
805 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
806 delete d->incubationController; d->incubationController = 0;
808 delete d->rootItem; d->rootItem = 0;
814 This function tries to release redundant resources currently held by the QML scene.
816 Calling this function might result in the scene graph and the OpenGL context used
817 for rendering being released to release graphics memory. If this happens, the
818 sceneGraphInvalidated() signal will be called, allowing users to clean up their
819 own graphics resources. The setPersistentOpenGLContext() and setPersistentSceneGraph()
820 functions can be used to prevent this from happening, if handling the cleanup is
821 not feasible in the application, at the cost of higher memory usage.
823 \sa sceneGraphInvalidated(), setPersistentOpenGLContext(), setPersistentSceneGraph().
826 void QQuickCanvas::releaseResources()
829 d->windowManager->releaseResources();
830 QQuickPixmap::purgeCache();
836 Controls whether the OpenGL context can be released as a part of a call to
839 The OpenGL context might still be released when the user makes an explicit
842 \sa setPersistentSceneGraph()
845 void QQuickCanvas::setPersistentOpenGLContext(bool persistent)
848 d->persistentGLContext = persistent;
853 Returns whether the OpenGL context can be released as a part of a call to
857 bool QQuickCanvas::isPersistentOpenGLContext() const
859 Q_D(const QQuickCanvas);
860 return d->persistentGLContext;
866 Controls whether the scene graph nodes and resources can be released as a
867 part of a call to releaseResources().
869 The scene graph nodes and resources might still be released when the user
870 makes an explicit call to hide().
872 \sa setPersistentOpenGLContext()
875 void QQuickCanvas::setPersistentSceneGraph(bool persistent)
878 d->persistentSceneGraph = persistent;
884 Returns whether the scene graph nodes and resources can be released as a part
885 of a call to releaseResources().
888 bool QQuickCanvas::isPersistentSceneGraph() const
890 Q_D(const QQuickCanvas);
891 return d->persistentSceneGraph;
899 Returns the invisible root item of the scene.
901 A QQuickCanvas always has a single invisible root item. To add items to this canvas,
902 reparent the items to the root item or to an existing item in the scene.
904 QQuickItem *QQuickCanvas::rootItem() const
906 Q_D(const QQuickCanvas);
912 Returns the item which currently has active focus.
914 QQuickItem *QQuickCanvas::activeFocusItem() const
916 Q_D(const QQuickCanvas);
918 return d->activeFocusItem;
921 QObject *QQuickCanvas::focusObject() const
923 Q_D(const QQuickCanvas);
925 if (d->activeFocusItem)
926 return d->activeFocusItem;
927 return const_cast<QQuickCanvas*>(this);
932 Returns the item which currently has the mouse grab.
934 QQuickItem *QQuickCanvas::mouseGrabberItem() const
936 Q_D(const QQuickCanvas);
938 return d->mouseGrabberItem;
943 \qmlproperty color QtQuick2.Window::Window::color
945 The background color for the window.
947 Setting this property is more efficient than using a separate Rectangle.
950 bool QQuickCanvasPrivate::clearHover()
952 if (hoverItems.isEmpty())
955 QPointF pos = QCursor::pos(); // ### refactor: q->mapFromGlobal(QCursor::pos());
957 bool accepted = false;
958 foreach (QQuickItem* item, hoverItems)
959 accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), true) || accepted;
965 bool QQuickCanvas::event(QEvent *e)
971 case QEvent::TouchBegin:
972 case QEvent::TouchUpdate:
973 case QEvent::TouchEnd:
974 case QEvent::TouchCancel:
976 QTouchEvent *touch = static_cast<QTouchEvent *>(e);
977 d->translateTouchEvent(touch);
978 d->deliverTouchEvent(touch);
979 if (qmlTranslateTouchToMouse())
980 d->translateTouchToMouse(touch);
982 return touch->isAccepted();
986 d->lastMousePosition = QPoint();
988 case QEvent::DragEnter:
989 case QEvent::DragLeave:
990 case QEvent::DragMove:
992 d->deliverDragEvent(&d->dragGrabber, e);
994 case QEvent::WindowDeactivate:
995 rootItem()->windowDeactivateEvent();
997 case QEvent::FocusAboutToChange:
998 if (d->activeFocusItem)
999 qGuiApp->inputMethod()->commit();
1005 return QWindow::event(e);
1009 void QQuickCanvas::keyPressEvent(QKeyEvent *e)
1013 if (d->activeFocusItem)
1014 sendEvent(d->activeFocusItem, e);
1018 void QQuickCanvas::keyReleaseEvent(QKeyEvent *e)
1022 if (d->activeFocusItem)
1023 sendEvent(d->activeFocusItem, e);
1026 bool QQuickCanvasPrivate::deliverInitialMousePressEvent(QQuickItem *item, QMouseEvent *event)
1030 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1031 if (itemPrivate->opacity() == 0.0)
1034 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1035 QPointF p = item->mapFromScene(event->windowPos());
1036 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
1040 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1041 for (int ii = children.count() - 1; ii >= 0; --ii) {
1042 QQuickItem *child = children.at(ii);
1043 if (!child->isVisible() || !child->isEnabled())
1045 if (deliverInitialMousePressEvent(child, event))
1049 if (itemPrivate->acceptedMouseButtons() & event->button()) {
1050 QPointF p = item->mapFromScene(event->windowPos());
1051 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1052 QMouseEvent me(event->type(), p, event->windowPos(), event->screenPos(),
1053 event->button(), event->buttons(), event->modifiers());
1056 q->sendEvent(item, &me);
1057 event->setAccepted(me.isAccepted());
1058 if (me.isAccepted())
1060 if (mouseGrabberItem)
1061 mouseGrabberItem->ungrabMouse();
1068 bool QQuickCanvasPrivate::deliverMouseEvent(QMouseEvent *event)
1072 lastMousePosition = event->windowPos();
1074 if (!mouseGrabberItem &&
1075 event->type() == QEvent::MouseButtonPress &&
1076 (event->buttons() & event->button()) == event->buttons()) {
1077 if (deliverInitialMousePressEvent(rootItem, event))
1081 return event->isAccepted();
1084 if (mouseGrabberItem) {
1085 QQuickItemPrivate *mgPrivate = QQuickItemPrivate::get(mouseGrabberItem);
1086 const QTransform &transform = mgPrivate->canvasToItemTransform();
1087 QQuickMouseEventEx me(event->type(), transform.map(event->windowPos()),
1088 event->windowPos(), event->screenPos(),
1089 event->button(), event->buttons(), event->modifiers());
1090 if (QQuickMouseEventEx::extended(event))
1091 me.setVelocity(QQuickMouseEventEx::extended(event)->velocity());
1093 q->sendEvent(mouseGrabberItem, &me);
1094 event->setAccepted(me.isAccepted());
1095 if (me.isAccepted())
1103 void QQuickCanvas::mousePressEvent(QMouseEvent *event)
1106 if (qmlTranslateTouchToMouse())
1107 return; // We are using touch events
1109 qWarning() << "QQuickCanvas::mousePressEvent()" << event->pos() << event->button() << event->buttons();
1112 d->deliverMouseEvent(event);
1116 void QQuickCanvas::mouseReleaseEvent(QMouseEvent *event)
1119 if (qmlTranslateTouchToMouse())
1120 return; // We are using touch events
1122 qWarning() << "QQuickCanvas::mouseReleaseEvent()" << event->pos() << event->button() << event->buttons();
1125 if (!d->mouseGrabberItem) {
1126 QWindow::mouseReleaseEvent(event);
1130 d->deliverMouseEvent(event);
1131 if (d->mouseGrabberItem)
1132 d->mouseGrabberItem->ungrabMouse();
1136 void QQuickCanvas::mouseDoubleClickEvent(QMouseEvent *event)
1139 if (qmlTranslateTouchToMouse())
1140 return; // We are using touch events
1143 qWarning() << "QQuickCanvas::mouseDoubleClickEvent()" << event->pos() << event->button() << event->buttons();
1146 if (!d->mouseGrabberItem && (event->buttons() & event->button()) == event->buttons()) {
1147 if (d->deliverInitialMousePressEvent(d->rootItem, event))
1154 d->deliverMouseEvent(event);
1157 bool QQuickCanvasPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
1158 const QPointF &scenePos, const QPointF &lastScenePos,
1159 Qt::KeyboardModifiers modifiers, bool accepted)
1162 const QTransform transform = QQuickItemPrivate::get(item)->canvasToItemTransform();
1164 //create copy of event
1165 QHoverEvent hoverEvent(type, transform.map(scenePos), transform.map(lastScenePos), modifiers);
1166 hoverEvent.setAccepted(accepted);
1168 q->sendEvent(item, &hoverEvent);
1170 return hoverEvent.isAccepted();
1174 void QQuickCanvas::mouseMoveEvent(QMouseEvent *event)
1177 if (qmlTranslateTouchToMouse())
1178 return; // We are using touch events
1180 qWarning() << "QQuickCanvas::mouseMoveEvent()" << event->pos() << event->button() << event->buttons();
1183 if (!d->mouseGrabberItem) {
1184 if (d->lastMousePosition.isNull())
1185 d->lastMousePosition = event->windowPos();
1186 QPointF last = d->lastMousePosition;
1187 d->lastMousePosition = event->windowPos();
1189 bool accepted = event->isAccepted();
1190 bool delivered = d->deliverHoverEvent(d->rootItem, event->windowPos(), last, event->modifiers(), accepted);
1192 //take care of any exits
1193 accepted = d->clearHover();
1195 event->setAccepted(accepted);
1199 d->deliverMouseEvent(event);
1202 bool QQuickCanvasPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
1203 Qt::KeyboardModifiers modifiers, bool &accepted)
1205 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1206 if (itemPrivate->opacity() == 0.0)
1209 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1210 QPointF p = item->mapFromScene(scenePos);
1211 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
1215 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1216 for (int ii = children.count() - 1; ii >= 0; --ii) {
1217 QQuickItem *child = children.at(ii);
1218 if (!child->isVisible() || !child->isEnabled())
1220 if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, accepted))
1224 if (itemPrivate->hoverEnabled) {
1225 QPointF p = item->mapFromScene(scenePos);
1226 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1227 if (!hoverItems.isEmpty() && hoverItems[0] == item) {
1229 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1231 QList<QQuickItem *> itemsToHover;
1232 QQuickItem* parent = item;
1233 itemsToHover << item;
1234 while ((parent = parent->parentItem()))
1235 itemsToHover << parent;
1237 // Leaving from previous hovered items until we reach the item or one of its ancestors.
1238 while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems[0])) {
1239 sendHoverEvent(QEvent::HoverLeave, hoverItems[0], scenePos, lastScenePos, modifiers, accepted);
1240 hoverItems.removeFirst();
1243 if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item
1244 // ### Shouldn't we send moves for the parent items as well?
1245 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1247 // Enter items that are not entered yet.
1249 if (!hoverItems.isEmpty())
1250 startIdx = itemsToHover.indexOf(hoverItems[0]) - 1;
1252 startIdx = itemsToHover.count() - 1;
1254 for (int i = startIdx; i >= 0; i--) {
1255 QQuickItem *itemToHover = itemsToHover[i];
1256 if (QQuickItemPrivate::get(itemToHover)->hoverEnabled) {
1257 hoverItems.prepend(itemToHover);
1258 sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, accepted);
1270 bool QQuickCanvasPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event)
1273 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1274 if (itemPrivate->opacity() == 0.0)
1277 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1278 QPointF p = item->mapFromScene(event->posF());
1279 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
1283 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1284 for (int ii = children.count() - 1; ii >= 0; --ii) {
1285 QQuickItem *child = children.at(ii);
1286 if (!child->isVisible() || !child->isEnabled())
1288 if (deliverWheelEvent(child, event))
1292 QPointF p = item->mapFromScene(event->posF());
1293 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1294 QWheelEvent wheel(p, p, event->pixelDelta(), event->angleDelta(), event->delta(),
1295 event->orientation(), event->buttons(), event->modifiers());
1297 q->sendEvent(item, &wheel);
1298 if (wheel.isAccepted()) {
1307 #ifndef QT_NO_WHEELEVENT
1309 void QQuickCanvas::wheelEvent(QWheelEvent *event)
1313 qWarning() << "QQuickCanvas::wheelEvent()" << event->pos() << event->delta() << event->orientation();
1316 d->deliverWheelEvent(d->rootItem, event);
1318 #endif // QT_NO_WHEELEVENT
1320 bool QQuickCanvasPrivate::deliverTouchEvent(QTouchEvent *event)
1323 if (event->type() == QEvent::TouchBegin)
1324 qWarning("touchBeginEvent");
1325 else if (event->type() == QEvent::TouchUpdate)
1326 qWarning("touchUpdateEvent");
1327 else if (event->type() == QEvent::TouchEnd)
1328 qWarning("touchEndEvent");
1329 else if (event->type() == QEvent::TouchCancel)
1330 qWarning("touchCancelEvent");
1335 QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > updatedPoints;
1337 if (event->type() == QTouchEvent::TouchBegin) { // all points are new touch points
1338 QSet<int> acceptedNewPoints;
1339 deliverTouchPoints(rootItem, event, event->touchPoints(), &acceptedNewPoints, &updatedPoints);
1340 if (acceptedNewPoints.count() > 0)
1344 return event->isAccepted();
1347 if (event->type() == QTouchEvent::TouchCancel) {
1348 // A TouchCancel event will typically not contain any points.
1349 // Deliver it to all items that have active touches.
1350 QSet<QQuickItem *> cancelDelivered;
1351 foreach (QQuickItem *item, itemForTouchPointId) {
1352 if (cancelDelivered.contains(item))
1354 cancelDelivered.insert(item);
1355 q->sendEvent(item, event);
1357 // The next touch event can only be a TouchBegin so clean up.
1358 itemForTouchPointId.clear();
1362 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
1363 QList<QTouchEvent::TouchPoint> newPoints;
1364 QQuickItem *item = 0;
1365 for (int i=0; i<touchPoints.count(); i++) {
1366 const QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
1367 switch (touchPoint.state()) {
1368 case Qt::TouchPointPressed:
1369 newPoints << touchPoint;
1371 case Qt::TouchPointMoved:
1372 case Qt::TouchPointStationary:
1373 case Qt::TouchPointReleased:
1374 if (itemForTouchPointId.contains(touchPoint.id())) {
1375 item = itemForTouchPointId[touchPoint.id()];
1377 updatedPoints[item].append(touchPoint);
1385 if (newPoints.count() > 0 || updatedPoints.count() > 0) {
1386 QSet<int> acceptedNewPoints;
1387 int prevCount = updatedPoints.count();
1388 deliverTouchPoints(rootItem, event, newPoints, &acceptedNewPoints, &updatedPoints);
1389 if (acceptedNewPoints.count() > 0 || updatedPoints.count() != prevCount)
1396 if (event->touchPointStates() & Qt::TouchPointReleased) {
1397 for (int i=0; i<touchPoints.count(); i++) {
1398 if (touchPoints[i].state() == Qt::TouchPointReleased)
1399 itemForTouchPointId.remove(touchPoints[i].id());
1403 return event->isAccepted();
1406 bool QQuickCanvasPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints, QSet<int> *acceptedNewPoints, QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *updatedPoints)
1409 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1411 if (itemPrivate->opacity() == 0.0)
1414 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1415 QRectF bounds(0, 0, item->width(), item->height());
1416 for (int i=0; i<newPoints.count(); i++) {
1417 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1418 if (!bounds.contains(p))
1423 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1424 for (int ii = children.count() - 1; ii >= 0; --ii) {
1425 QQuickItem *child = children.at(ii);
1426 if (!child->isEnabled() || !child->isVisible())
1428 if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints))
1432 QList<QTouchEvent::TouchPoint> matchingPoints;
1433 if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) {
1434 QRectF bounds(0, 0, item->width(), item->height());
1435 for (int i=0; i<newPoints.count(); i++) {
1436 if (acceptedNewPoints->contains(newPoints[i].id()))
1438 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1439 if (bounds.contains(p))
1440 matchingPoints << newPoints[i];
1444 if (matchingPoints.count() > 0 || (*updatedPoints)[item].count() > 0) {
1445 QList<QTouchEvent::TouchPoint> &eventPoints = (*updatedPoints)[item];
1446 eventPoints.append(matchingPoints);
1447 transformTouchPoints(eventPoints, itemPrivate->canvasToItemTransform());
1449 Qt::TouchPointStates eventStates;
1450 for (int i=0; i<eventPoints.count(); i++)
1451 eventStates |= eventPoints[i].state();
1452 // if all points have the same state, set the event type accordingly
1453 QEvent::Type eventType;
1454 switch (eventStates) {
1455 case Qt::TouchPointPressed:
1456 eventType = QEvent::TouchBegin;
1458 case Qt::TouchPointReleased:
1459 eventType = QEvent::TouchEnd;
1462 eventType = QEvent::TouchUpdate;
1466 if (eventStates != Qt::TouchPointStationary) {
1467 QTouchEvent touchEvent(eventType);
1468 touchEvent.setWindow(event->window());
1469 touchEvent.setTarget(item);
1470 touchEvent.setDevice(event->device());
1471 touchEvent.setModifiers(event->modifiers());
1472 touchEvent.setTouchPointStates(eventStates);
1473 touchEvent.setTouchPoints(eventPoints);
1474 touchEvent.setTimestamp(event->timestamp());
1476 touchEvent.accept();
1477 q->sendEvent(item, &touchEvent);
1479 if (touchEvent.isAccepted()) {
1480 for (int i=0; i<matchingPoints.count(); i++) {
1481 itemForTouchPointId[matchingPoints[i].id()] = item;
1482 acceptedNewPoints->insert(matchingPoints[i].id());
1488 updatedPoints->remove(item);
1489 if (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty())
1495 void QQuickCanvasPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event)
1498 grabber->resetTarget();
1499 QQuickDragGrabber::iterator grabItem = grabber->begin();
1500 if (grabItem != grabber->end()) {
1501 Q_ASSERT(event->type() != QEvent::DragEnter);
1502 if (event->type() == QEvent::Drop) {
1503 QDropEvent *e = static_cast<QDropEvent *>(event);
1504 for (e->setAccepted(false); !e->isAccepted() && grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1505 QPointF p = (**grabItem)->mapFromScene(e->pos());
1506 QDropEvent translatedEvent(
1508 e->possibleActions(),
1511 e->keyboardModifiers());
1512 QQuickDropEventEx::copyActions(&translatedEvent, *e);
1513 q->sendEvent(**grabItem, &translatedEvent);
1514 e->setAccepted(translatedEvent.isAccepted());
1515 e->setDropAction(translatedEvent.dropAction());
1516 grabber->setTarget(**grabItem);
1519 if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave.
1520 QDragLeaveEvent leaveEvent;
1521 for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
1522 q->sendEvent(**grabItem, &leaveEvent);
1524 } else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1525 QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event);
1526 if (deliverDragEvent(grabber, **grabItem, moveEvent)) {
1527 moveEvent->setAccepted(true);
1528 for (++grabItem; grabItem != grabber->end();) {
1529 QPointF p = (**grabItem)->mapFromScene(moveEvent->pos());
1530 if (QRectF(0, 0, (**grabItem)->width(), (**grabItem)->height()).contains(p)) {
1531 QDragMoveEvent translatedEvent(
1533 moveEvent->possibleActions(),
1534 moveEvent->mimeData(),
1535 moveEvent->mouseButtons(),
1536 moveEvent->keyboardModifiers());
1537 QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent);
1538 q->sendEvent(**grabItem, &translatedEvent);
1541 QDragLeaveEvent leaveEvent;
1542 q->sendEvent(**grabItem, &leaveEvent);
1543 grabItem = grabber->release(grabItem);
1548 QDragLeaveEvent leaveEvent;
1549 q->sendEvent(**grabItem, &leaveEvent);
1553 if (event->type() == QEvent::DragEnter || event->type() == QEvent::DragMove) {
1554 QDragMoveEvent *e = static_cast<QDragMoveEvent *>(event);
1555 QDragEnterEvent enterEvent(
1557 e->possibleActions(),
1560 e->keyboardModifiers());
1561 QQuickDropEventEx::copyActions(&enterEvent, *e);
1562 event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent));
1566 bool QQuickCanvasPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event)
1569 bool accepted = false;
1570 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1571 if (itemPrivate->opacity() == 0.0 || !item->isVisible() || !item->isEnabled())
1574 QPointF p = item->mapFromScene(event->pos());
1575 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1576 if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) {
1577 QDragMoveEvent translatedEvent(
1579 event->possibleActions(),
1581 event->mouseButtons(),
1582 event->keyboardModifiers(),
1584 QQuickDropEventEx::copyActions(&translatedEvent, *event);
1585 q->sendEvent(item, &translatedEvent);
1586 if (event->type() == QEvent::DragEnter) {
1587 if (translatedEvent.isAccepted()) {
1588 grabber->grab(item);
1595 } else if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1599 QDragEnterEvent enterEvent(
1601 event->possibleActions(),
1603 event->mouseButtons(),
1604 event->keyboardModifiers());
1605 QQuickDropEventEx::copyActions(&enterEvent, *event);
1606 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1607 for (int ii = children.count() - 1; ii >= 0; --ii) {
1608 if (deliverDragEvent(grabber, children.at(ii), &enterEvent))
1615 bool QQuickCanvasPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QEvent *event)
1620 QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target);
1621 if (targetPrivate->filtersChildMouseEvents)
1622 if (target->childMouseEventFilter(item, event))
1625 if (sendFilteredMouseEvent(target->parentItem(), item, event))
1632 Propagates an event to a QQuickItem on the canvas
1634 bool QQuickCanvas::sendEvent(QQuickItem *item, QEvent *e)
1639 qWarning("QQuickCanvas::sendEvent: Cannot send event to a null item");
1645 switch (e->type()) {
1646 case QEvent::KeyPress:
1647 case QEvent::KeyRelease:
1649 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1650 while (!e->isAccepted() && (item = item->parentItem())) {
1652 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1655 case QEvent::FocusIn:
1656 case QEvent::FocusOut:
1657 QQuickItemPrivate::get(item)->deliverFocusEvent(static_cast<QFocusEvent *>(e));
1659 case QEvent::MouseButtonPress:
1660 case QEvent::MouseButtonRelease:
1661 case QEvent::MouseButtonDblClick:
1662 case QEvent::MouseMove:
1663 // XXX todo - should sendEvent be doing this? how does it relate to forwarded events?
1664 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
1666 QQuickItemPrivate::get(item)->deliverMouseEvent(static_cast<QMouseEvent *>(e));
1669 case QEvent::UngrabMouse:
1670 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
1672 item->mouseUngrabEvent();
1676 QQuickItemPrivate::get(item)->deliverWheelEvent(static_cast<QWheelEvent *>(e));
1678 case QEvent::HoverEnter:
1679 case QEvent::HoverLeave:
1680 case QEvent::HoverMove:
1681 QQuickItemPrivate::get(item)->deliverHoverEvent(static_cast<QHoverEvent *>(e));
1683 case QEvent::TouchBegin:
1684 case QEvent::TouchUpdate:
1685 case QEvent::TouchEnd:
1686 case QEvent::TouchCancel:
1687 // XXX todo - should sendEvent be doing this? how does it relate to forwarded events?
1688 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
1690 QQuickItemPrivate::get(item)->deliverTouchEvent(static_cast<QTouchEvent *>(e));
1693 case QEvent::DragEnter:
1694 case QEvent::DragMove:
1695 case QEvent::DragLeave:
1697 QQuickItemPrivate::get(item)->deliverDragEvent(e);
1706 void QQuickCanvasPrivate::cleanupNodes()
1708 for (int ii = 0; ii < cleanupNodeList.count(); ++ii)
1709 delete cleanupNodeList.at(ii);
1710 cleanupNodeList.clear();
1713 void QQuickCanvasPrivate::cleanupNodesOnShutdown(QQuickItem *item)
1715 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
1716 if (p->itemNodeInstance) {
1717 delete p->itemNodeInstance;
1718 p->itemNodeInstance = 0;
1720 if (p->extra.isAllocated()) {
1721 p->extra->opacityNode = 0;
1722 p->extra->clipNode = 0;
1723 p->extra->rootNode = 0;
1730 for (int ii = 0; ii < p->childItems.count(); ++ii)
1731 cleanupNodesOnShutdown(p->childItems.at(ii));
1734 // This must be called from the render thread, with the main thread frozen
1735 void QQuickCanvasPrivate::cleanupNodesOnShutdown()
1738 cleanupNodesOnShutdown(rootItem);
1739 QSet<QQuickItem *>::const_iterator it = parentlessItems.begin();
1740 for (; it != parentlessItems.end(); ++it)
1741 cleanupNodesOnShutdown(*it);
1744 void QQuickCanvasPrivate::updateDirtyNodes()
1747 qWarning() << "QQuickCanvasPrivate::updateDirtyNodes():";
1752 QQuickItem *updateList = dirtyItemList;
1754 if (updateList) QQuickItemPrivate::get(updateList)->prevDirtyItem = &updateList;
1756 while (updateList) {
1757 QQuickItem *item = updateList;
1758 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
1759 itemPriv->removeFromDirtyList();
1762 qWarning() << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString());
1764 updateDirtyNode(item);
1768 void QQuickCanvasPrivate::updateDirtyNode(QQuickItem *item)
1770 #ifdef QML_RUNTIME_TESTING
1771 bool didFlash = false;
1774 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
1775 quint32 dirty = itemPriv->dirtyAttributes;
1776 itemPriv->dirtyAttributes = 0;
1778 if ((dirty & QQuickItemPrivate::TransformUpdateMask) ||
1779 (dirty & QQuickItemPrivate::Size && itemPriv->origin() != QQuickItem::TopLeft &&
1780 (itemPriv->scale() != 1. || itemPriv->rotation() != 0.))) {
1784 if (itemPriv->x != 0. || itemPriv->y != 0.)
1785 matrix.translate(itemPriv->x, itemPriv->y);
1787 for (int ii = itemPriv->transforms.count() - 1; ii >= 0; --ii)
1788 itemPriv->transforms.at(ii)->applyTo(&matrix);
1790 if (itemPriv->scale() != 1. || itemPriv->rotation() != 0.) {
1791 QPointF origin = item->transformOriginPoint();
1792 matrix.translate(origin.x(), origin.y());
1793 if (itemPriv->scale() != 1.)
1794 matrix.scale(itemPriv->scale(), itemPriv->scale());
1795 if (itemPriv->rotation() != 0.)
1796 matrix.rotate(itemPriv->rotation(), 0, 0, 1);
1797 matrix.translate(-origin.x(), -origin.y());
1800 itemPriv->itemNode()->setMatrix(matrix);
1803 bool clipEffectivelyChanged = (dirty & (QQuickItemPrivate::Clip | QQuickItemPrivate::Canvas)) &&
1804 ((item->clip() == false) != (itemPriv->clipNode() == 0));
1805 int effectRefCount = itemPriv->extra.isAllocated()?itemPriv->extra->effectRefCount:0;
1806 bool effectRefEffectivelyChanged = (dirty & (QQuickItemPrivate::EffectReference | QQuickItemPrivate::Canvas)) &&
1807 ((effectRefCount == 0) != (itemPriv->rootNode() == 0));
1809 if (clipEffectivelyChanged) {
1810 QSGNode *parent = itemPriv->opacityNode() ? (QSGNode *) itemPriv->opacityNode() :
1811 (QSGNode *)itemPriv->itemNode();
1812 QSGNode *child = itemPriv->rootNode() ? (QSGNode *)itemPriv->rootNode() :
1813 (QSGNode *)itemPriv->groupNode;
1816 Q_ASSERT(itemPriv->clipNode() == 0);
1817 itemPriv->extra.value().clipNode = new QQuickDefaultClipNode(item->clipRect());
1818 itemPriv->clipNode()->update();
1821 parent->removeChildNode(child);
1822 parent->appendChildNode(itemPriv->clipNode());
1824 itemPriv->clipNode()->appendChildNode(child);
1827 Q_ASSERT(itemPriv->clipNode() != 0);
1828 parent->removeChildNode(itemPriv->clipNode());
1830 itemPriv->clipNode()->removeChildNode(child);
1831 delete itemPriv->clipNode();
1832 itemPriv->extra->clipNode = 0;
1834 parent->appendChildNode(child);
1838 if (dirty & QQuickItemPrivate::ChildrenUpdateMask)
1839 itemPriv->childContainerNode()->removeAllChildNodes();
1841 if (effectRefEffectivelyChanged) {
1842 QSGNode *parent = itemPriv->clipNode();
1844 parent = itemPriv->opacityNode();
1846 parent = itemPriv->itemNode();
1847 QSGNode *child = itemPriv->groupNode;
1849 if (itemPriv->extra.isAllocated() && itemPriv->extra->effectRefCount) {
1850 Q_ASSERT(itemPriv->rootNode() == 0);
1851 itemPriv->extra->rootNode = new QSGRootNode;
1854 parent->removeChildNode(child);
1855 parent->appendChildNode(itemPriv->rootNode());
1857 itemPriv->rootNode()->appendChildNode(child);
1859 Q_ASSERT(itemPriv->rootNode() != 0);
1860 parent->removeChildNode(itemPriv->rootNode());
1862 itemPriv->rootNode()->removeChildNode(child);
1863 delete itemPriv->rootNode();
1864 itemPriv->extra->rootNode = 0;
1866 parent->appendChildNode(child);
1870 if (dirty & QQuickItemPrivate::ChildrenUpdateMask) {
1871 QSGNode *groupNode = itemPriv->groupNode;
1873 groupNode->removeAllChildNodes();
1875 QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems();
1878 for (; ii < orderedChildren.count() && orderedChildren.at(ii)->z() < 0; ++ii) {
1879 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
1880 if (!childPrivate->explicitVisible &&
1881 (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount))
1883 if (childPrivate->itemNode()->parent())
1884 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
1886 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
1889 QSGNode *beforePaintNode = itemPriv->groupNode ? itemPriv->groupNode->lastChild() : 0;
1890 if (beforePaintNode || itemPriv->extra.isAllocated())
1891 itemPriv->extra.value().beforePaintNode = beforePaintNode;
1893 if (itemPriv->paintNode)
1894 itemPriv->childContainerNode()->appendChildNode(itemPriv->paintNode);
1896 for (; ii < orderedChildren.count(); ++ii) {
1897 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
1898 if (!childPrivate->explicitVisible &&
1899 (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount))
1901 if (childPrivate->itemNode()->parent())
1902 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
1904 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
1908 if ((dirty & QQuickItemPrivate::Size) && itemPriv->clipNode()) {
1909 itemPriv->clipNode()->setRect(item->clipRect());
1910 itemPriv->clipNode()->update();
1913 if (dirty & (QQuickItemPrivate::OpacityValue | QQuickItemPrivate::Visible
1914 | QQuickItemPrivate::HideReference | QQuickItemPrivate::Canvas))
1916 qreal opacity = itemPriv->explicitVisible && (!itemPriv->extra.isAllocated() || itemPriv->extra->hideRefCount == 0)
1917 ? itemPriv->opacity() : qreal(0);
1919 if (opacity != 1 && !itemPriv->opacityNode()) {
1920 itemPriv->extra.value().opacityNode = new QSGOpacityNode;
1922 QSGNode *parent = itemPriv->itemNode();
1923 QSGNode *child = itemPriv->clipNode();
1925 child = itemPriv->rootNode();
1927 child = itemPriv->groupNode;
1930 parent->removeChildNode(child);
1931 parent->appendChildNode(itemPriv->opacityNode());
1933 itemPriv->opacityNode()->appendChildNode(child);
1935 if (itemPriv->opacityNode())
1936 itemPriv->opacityNode()->setOpacity(opacity);
1939 if (dirty & QQuickItemPrivate::ContentUpdateMask) {
1941 if (itemPriv->flags & QQuickItem::ItemHasContents) {
1942 updatePaintNodeData.transformNode = itemPriv->itemNode();
1943 itemPriv->paintNode = item->updatePaintNode(itemPriv->paintNode, &updatePaintNodeData);
1945 Q_ASSERT(itemPriv->paintNode == 0 ||
1946 itemPriv->paintNode->parent() == 0 ||
1947 itemPriv->paintNode->parent() == itemPriv->childContainerNode());
1949 if (itemPriv->paintNode && itemPriv->paintNode->parent() == 0) {
1950 if (itemPriv->extra.isAllocated() && itemPriv->extra->beforePaintNode)
1951 itemPriv->childContainerNode()->insertChildNodeAfter(itemPriv->paintNode, itemPriv->extra->beforePaintNode);
1953 itemPriv->childContainerNode()->prependChildNode(itemPriv->paintNode);
1955 } else if (itemPriv->paintNode) {
1956 delete itemPriv->paintNode;
1957 itemPriv->paintNode = 0;
1961 if ((dirty & (QQuickItemPrivate::PerformanceHints | QQuickItemPrivate::Canvas)) && itemPriv->groupNode) {
1962 itemPriv->groupNode->setFlag(QSGNode::ChildrenDoNotOverlap, itemPriv->childrenDoNotOverlap);
1963 itemPriv->groupNode->setFlag(QSGNode::StaticSubtreeGeometry, itemPriv->staticSubtreeGeometry);
1967 // Check consistency.
1968 const QSGNode *nodeChain[] = {
1969 itemPriv->itemNodeInstance,
1970 itemPriv->opacityNode(),
1971 itemPriv->clipNode(),
1972 itemPriv->rootNode(),
1973 itemPriv->groupNode,
1974 itemPriv->paintNode,
1979 while (ip < 5 && nodeChain[ip] == 0)
1984 while (ic < 5 && nodeChain[ic] == 0)
1986 const QSGNode *parent = nodeChain[ip];
1987 const QSGNode *child = nodeChain[ic];
1989 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 0);
1991 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 1);
1992 Q_ASSERT(child->parent() == parent);
1993 bool containsChild = false;
1994 for (QSGNode *n = parent->firstChild(); n; n = n->nextSibling())
1995 containsChild |= (n == child);
1996 Q_ASSERT(containsChild);
2002 #ifdef QML_RUNTIME_TESTING
2003 if (itemPriv->sceneGraphContext()->isFlashModeEnabled()) {
2004 QSGFlashNode *flash = new QSGFlashNode();
2005 flash->setRect(item->boundingRect());
2006 itemPriv->childContainerNode()->appendChildNode(flash);
2017 void QQuickCanvas::maybeUpdate()
2020 d->windowManager->maybeUpdate(this);
2023 void QQuickCanvas::cleanupSceneGraph()
2030 delete d->renderer->rootNode();
2037 Returns the opengl context used for rendering.
2039 If the scene graph is not ready, this function will return 0.
2041 \sa sceneGraphInitialized(), sceneGraphInvalidated()
2044 QOpenGLContext *QQuickCanvas::openglContext() const
2046 Q_D(const QQuickCanvas);
2047 if (d->context->isReady())
2048 return d->context->glContext();
2054 \fn void QSGContext::sceneGraphInitialized()
2056 This signal is emitted when the scene graph has been initialized.
2058 This signal will be emitted from the scene graph rendering thread.
2064 \fn void QSGContext::sceneGraphInvalidated()
2066 This signal is emitted when the scene graph has been invalidated.
2068 This signal implies that the opengl rendering context used
2069 has been invalidated and all user resources tied to that context
2072 This signal will be emitted from the scene graph rendering thread.
2076 Returns the QSGEngine used for this scene.
2078 The engine will only be available once the scene graph has been
2079 initialized. Register for the sceneGraphEngine() signal to get
2080 notification about this.
2085 QSGEngine *QQuickCanvas::sceneGraphEngine() const
2087 Q_D(const QQuickCanvas);
2088 qWarning("QQuickCanvas::sceneGraphEngine() is deprecated, use members of QQuickCanvas instead");
2089 if (d->context && d->context->isReady())
2097 Sets the render target for this canvas to be \a fbo.
2099 The specified fbo must be created in the context of the canvas
2100 or one that shares with it.
2103 This function can only be called from the thread doing
2107 void QQuickCanvas::setRenderTarget(QOpenGLFramebufferObject *fbo)
2110 if (d->context && d->context && QThread::currentThread() != d->context->thread()) {
2111 qWarning("QQuickCanvas::setRenderThread: Cannot set render target from outside the rendering thread");
2115 d->renderTarget = fbo;
2117 d->renderTargetId = fbo->handle();
2118 d->renderTargetSize = fbo->size();
2120 d->renderTargetId = 0;
2121 d->renderTargetSize = QSize();
2128 void QQuickCanvas::setRenderTarget(uint fboId, const QSize &size)
2131 if (d->context && d->context && QThread::currentThread() != d->context->thread()) {
2132 qWarning("QQuickCanvas::setRenderThread: Cannot set render target from outside the rendering thread");
2136 d->renderTargetId = fboId;
2137 d->renderTargetSize = size;
2139 // Unset any previously set instance...
2140 d->renderTarget = 0;
2145 Returns the FBO id of the render target when set; otherwise returns 0.
2147 uint QQuickCanvas::renderTargetId() const
2149 Q_D(const QQuickCanvas);
2150 return d->renderTargetId;
2154 Returns the size of the currently set render target; otherwise returns an enpty size.
2156 QSize QQuickCanvas::renderTargetSize() const
2158 Q_D(const QQuickCanvas);
2159 return d->renderTargetSize;
2166 Returns the render target for this canvas.
2168 The default is to render to the surface of the canvas, in which
2169 case the render target is 0.
2171 QOpenGLFramebufferObject *QQuickCanvas::renderTarget() const
2173 Q_D(const QQuickCanvas);
2174 return d->renderTarget;
2179 Grabs the contents of the framebuffer and returns it as an image.
2181 This function might not work if the view is not visible.
2183 \warning Calling this function will cause performance problems.
2185 \warning This function can only be called from the GUI thread.
2187 QImage QQuickCanvas::grabFrameBuffer()
2190 return d->windowManager->grab(this);
2194 Returns an incubation controller that splices incubation between frames
2195 for this canvas. QQuickView automatically installs this controller for you,
2196 otherwise you will need to install it yourself using \l{QQmlEngine::setIncubationController}
2198 The controller is owned by the canvas and will be destroyed when the canvas
2201 QQmlIncubationController *QQuickCanvas::incubationController() const
2203 Q_D(const QQuickCanvas);
2205 if (!d->incubationController)
2206 d->incubationController = new QQuickCanvasIncubationController(const_cast<QQuickCanvasPrivate *>(d));
2207 return d->incubationController;
2213 \enum QQuickCanvas::CreateTextureOption
2215 The CreateTextureOption enums are used to customize a texture is wrapped.
2217 \value TextureHasAlphaChannel The texture has an alpha channel and should
2218 be drawn using blending.
2220 \value TextureHasMipmaps The texture has mipmaps and can be drawn with
2223 \value TextureOwnsGLTexture The texture object owns the texture id and
2224 will delete the GL texture when the texture object is deleted.
2228 \fn void QQuickCanvas::beforeRendering()
2230 This signal is emitted before the scene starts rendering.
2232 Combined with the modes for clearing the background, this option
2233 can be used to paint using raw GL under QML content.
2235 The GL context used for rendering the scene graph will be bound
2238 \warning Since this signal is emitted from the scene graph rendering thread, the
2239 receiver should be on the scene graph thread or the connection should be Qt::DirectConnection.
2241 \warning Make very sure that a signal handler for beforeRendering leaves the GL
2242 context in the same state as it was when the signal handler was entered. Failing to
2243 do so can result in the scene not rendering properly.
2247 \fn void QQuickCanvas::afterRendering()
2249 This signal is emitted after the scene has completed rendering, before swapbuffers is called.
2251 This signal can be used to paint using raw GL on top of QML content,
2252 or to do screen scraping of the current frame buffer.
2254 The GL context used for rendering the scene graph will be bound at this point.
2256 \warning Since this signal is emitted from the scene graph rendering thread, the
2257 receiver should be on the scene graph thread or the connection should be Qt::DirectConnection.
2259 \warning Make very sure that a signal handler for afterRendering() leaves the GL
2260 context in the same state as it was when the signal handler was entered. Failing to
2261 do so can result in the scene not rendering properly.
2267 Sets weither the scene graph rendering of QML should clear the color buffer
2268 before it starts rendering to \a enbled.
2270 By disabling clearing of the color buffer, it is possible to do GL painting
2271 under the scene graph.
2273 The color buffer is cleared by default.
2275 \sa beforeRendering()
2278 void QQuickCanvas::setClearBeforeRendering(bool enabled)
2281 d->clearBeforeRendering = enabled;
2287 Returns weither clearing of the color buffer is done before rendering or not.
2290 bool QQuickCanvas::clearBeforeRendering() const
2292 Q_D(const QQuickCanvas);
2293 return d->clearBeforeRendering;
2299 Creates a new QSGTexture from the supplied \a image. If the image has an
2300 alpha channel, the corresponding texture will have an alpha channel.
2302 The caller of the function is responsible for deleting the returned texture.
2303 The actual GL texture will be deleted when the texture object is deleted.
2305 \warning This function will return 0 if the scene graph has not yet been
2308 This function can be called both from the GUI thread and the rendering thread.
2310 \sa sceneGraphInitialized()
2313 QSGTexture *QQuickCanvas::createTextureFromImage(const QImage &image) const
2315 Q_D(const QQuickCanvas);
2316 if (d->context && d->context->isReady())
2317 return d->context->createTexture(image);
2325 Creates a new QSGTexture object from an existing GL texture \a id.
2327 The caller of the function is responsible for deleting the returned texture.
2329 Use \a options to customize the texture attributes.
2331 \warning This function will return 0 if the scenegraph has not yet been
2334 \sa sceneGraphInitialized()
2336 QSGTexture *QQuickCanvas::createTextureFromId(uint id, const QSize &size, CreateTextureOptions options) const
2338 Q_D(const QQuickCanvas);
2339 if (d->context && d->context->isReady()) {
2340 QSGPlainTexture *texture = new QSGPlainTexture();
2341 texture->setTextureId(id);
2342 texture->setHasAlphaChannel(options & TextureHasAlphaChannel);
2343 texture->setHasMipmaps(options & TextureHasMipmaps);
2344 texture->setOwnsTexture(options & TextureOwnsGLTexture);
2345 texture->setTextureSize(size);
2353 Sets the color used to clear the opengl context to \a color.
2355 Setting the clear color has no effect when clearing is disabled.
2357 \sa setClearBeforeRendering()
2360 void QQuickCanvas::setClearColor(const QColor &color)
2363 if (color == d->clearColor)
2366 d->clearColor = color;
2367 emit clearColorChanged(color);
2373 Returns the color used to clear the opengl context.
2376 QColor QQuickCanvas::clearColor() const
2378 return d_func()->clearColor;
2383 #include "moc_qquickcanvas.cpp"