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();
393 QQuickMouseEventEx me = touchToMouseEvent(QEvent::MouseButtonDblClick, p);
394 me.setTimestamp(event->timestamp());
395 me.setAccepted(false);
396 me.setCapabilities(event->device()->capabilities());
397 if (!mouseGrabberItem) {
398 if (deliverInitialMousePressEvent(rootItem, &me)) {
399 touchMouseId = p.id();
400 event->setAccepted(true);
403 deliverMouseEvent(&me);
404 if (me.isAccepted()) {
405 touchMouseId = p.id();
406 event->setAccepted(true);
410 QQuickMouseEventEx me = touchToMouseEvent(QEvent::MouseButtonPress, p);
411 me.setTimestamp(event->timestamp());
412 me.setAccepted(false);
413 me.setCapabilities(event->device()->capabilities());
414 deliverMouseEvent(&me);
415 if (me.isAccepted()) {
416 touchMouseId = p.id();
417 event->setAccepted(true);
419 if (touchMouseId != -1)
421 } else if (p.id() == touchMouseId) {
422 if (p.state() & Qt::TouchPointMoved) {
423 QQuickMouseEventEx me = touchToMouseEvent(QEvent::MouseMove, p);
424 me.setTimestamp(event->timestamp());
425 me.setCapabilities(event->device()->capabilities());
426 if (!mouseGrabberItem) {
427 if (lastMousePosition.isNull())
428 lastMousePosition = me.windowPos();
429 QPointF last = lastMousePosition;
430 lastMousePosition = me.windowPos();
432 bool accepted = me.isAccepted();
433 bool delivered = deliverHoverEvent(rootItem, me.windowPos(), last, me.modifiers(), accepted);
435 //take care of any exits
436 accepted = clearHover();
438 me.setAccepted(accepted);
442 deliverMouseEvent(&me);
443 } else if (p.state() & Qt::TouchPointReleased) {
445 if (!mouseGrabberItem)
447 QQuickMouseEventEx me = touchToMouseEvent(QEvent::MouseButtonRelease, p);
448 me.setTimestamp(event->timestamp());
449 me.setCapabilities(event->device()->capabilities());
450 deliverMouseEvent(&me);
451 if (mouseGrabberItem)
452 mouseGrabberItem->ungrabMouse();
459 void QQuickCanvasPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform)
461 for (int i=0; i<touchPoints.count(); i++) {
462 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
463 touchPoint.setRect(transform.mapRect(touchPoint.sceneRect()));
464 touchPoint.setStartPos(transform.map(touchPoint.startScenePos()));
465 touchPoint.setLastPos(transform.map(touchPoint.lastScenePos()));
471 Translates the data in \a touchEvent to this canvas. This method leaves the item local positions in
472 \a touchEvent untouched (these are filled in later).
474 void QQuickCanvasPrivate::translateTouchEvent(QTouchEvent *touchEvent)
476 // Q_Q(QQuickCanvas);
478 // touchEvent->setWidget(q); // ### refactor...
480 QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
481 for (int i = 0; i < touchPoints.count(); ++i) {
482 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
484 touchPoint.setScreenRect(touchPoint.sceneRect());
485 touchPoint.setStartScreenPos(touchPoint.startScenePos());
486 touchPoint.setLastScreenPos(touchPoint.lastScenePos());
488 touchPoint.setSceneRect(touchPoint.rect());
489 touchPoint.setStartScenePos(touchPoint.startPos());
490 touchPoint.setLastScenePos(touchPoint.lastPos());
493 lastMousePosition = touchPoint.pos().toPoint();
495 touchEvent->setTouchPoints(touchPoints);
498 void QQuickCanvasPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
503 Q_ASSERT(scope || item == rootItem);
506 qWarning() << "QQuickCanvasPrivate::setFocusInScope():";
507 qWarning() << " scope:" << (QObject *)scope;
509 qWarning() << " scopeSubFocusItem:" << (QObject *)QQuickItemPrivate::get(scope)->subFocusItem;
510 qWarning() << " item:" << (QObject *)item;
511 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
514 QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0;
515 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
517 QQuickItem *oldActiveFocusItem = 0;
518 QQuickItem *newActiveFocusItem = 0;
520 QVarLengthArray<QQuickItem *, 20> changed;
522 // Does this change the active focus?
523 if (item == rootItem || (scopePrivate->activeFocus && item->isEnabled())) {
524 oldActiveFocusItem = activeFocusItem;
525 newActiveFocusItem = item;
526 while (newActiveFocusItem->isFocusScope()
527 && newActiveFocusItem->scopedFocusItem()
528 && newActiveFocusItem->scopedFocusItem()->isEnabled()) {
529 newActiveFocusItem = newActiveFocusItem->scopedFocusItem();
532 if (oldActiveFocusItem) {
534 qApp->inputMethod()->reset();
538 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
539 q->sendEvent(oldActiveFocusItem, &event);
541 QQuickItem *afi = oldActiveFocusItem;
542 while (afi && afi != scope) {
543 if (QQuickItemPrivate::get(afi)->activeFocus) {
544 QQuickItemPrivate::get(afi)->activeFocus = false;
547 afi = afi->parentItem();
552 if (item != rootItem && !(options & DontChangeSubFocusItem)) {
553 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
554 if (oldSubFocusItem) {
555 QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
556 changed << oldSubFocusItem;
559 QQuickItemPrivate::get(item)->updateSubFocusItem(scope, true);
562 if (!(options & DontChangeFocusProperty)) {
563 // if (item != rootItem || QGuiApplication::focusWindow() == q) { // QTBUG-22415
564 itemPrivate->focus = true;
569 if (newActiveFocusItem && rootItem->hasFocus()) {
570 activeFocusItem = newActiveFocusItem;
572 QQuickItemPrivate::get(newActiveFocusItem)->activeFocus = true;
573 changed << newActiveFocusItem;
575 QQuickItem *afi = newActiveFocusItem->parentItem();
576 while (afi && afi != scope) {
577 if (afi->isFocusScope()) {
578 QQuickItemPrivate::get(afi)->activeFocus = true;
581 afi = afi->parentItem();
584 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
585 q->sendEvent(newActiveFocusItem, &event);
588 emit q->focusObjectChanged(activeFocusItem);
590 if (!changed.isEmpty())
591 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
594 void QQuickCanvasPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
599 Q_ASSERT(scope || item == rootItem);
602 qWarning() << "QQuickCanvasPrivate::clearFocusInScope():";
603 qWarning() << " scope:" << (QObject *)scope;
604 qWarning() << " item:" << (QObject *)item;
605 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
608 QQuickItemPrivate *scopePrivate = 0;
610 scopePrivate = QQuickItemPrivate::get(scope);
611 if ( !scopePrivate->subFocusItem )
612 return;//No focus, nothing to do.
615 QQuickItem *oldActiveFocusItem = 0;
616 QQuickItem *newActiveFocusItem = 0;
618 QVarLengthArray<QQuickItem *, 20> changed;
620 Q_ASSERT(item == rootItem || item == scopePrivate->subFocusItem);
622 // Does this change the active focus?
623 if (item == rootItem || scopePrivate->activeFocus) {
624 oldActiveFocusItem = activeFocusItem;
625 newActiveFocusItem = scope;
627 Q_ASSERT(oldActiveFocusItem);
630 qApp->inputMethod()->reset();
634 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
635 q->sendEvent(oldActiveFocusItem, &event);
637 QQuickItem *afi = oldActiveFocusItem;
638 while (afi && afi != scope) {
639 if (QQuickItemPrivate::get(afi)->activeFocus) {
640 QQuickItemPrivate::get(afi)->activeFocus = false;
643 afi = afi->parentItem();
647 if (item != rootItem && !(options & DontChangeSubFocusItem)) {
648 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
649 if (oldSubFocusItem && !(options & DontChangeFocusProperty)) {
650 QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
651 changed << oldSubFocusItem;
654 QQuickItemPrivate::get(item)->updateSubFocusItem(scope, false);
656 } else if (!(options & DontChangeFocusProperty)) {
657 QQuickItemPrivate::get(item)->focus = false;
661 if (newActiveFocusItem) {
662 Q_ASSERT(newActiveFocusItem == scope);
663 activeFocusItem = scope;
665 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
666 q->sendEvent(newActiveFocusItem, &event);
669 emit q->focusObjectChanged(activeFocusItem);
671 if (!changed.isEmpty())
672 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
675 void QQuickCanvasPrivate::notifyFocusChangesRecur(QQuickItem **items, int remaining)
677 QQmlGuard<QQuickItem> item(*items);
680 notifyFocusChangesRecur(items + 1, remaining - 1);
683 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
685 if (itemPrivate->notifiedFocus != itemPrivate->focus) {
686 itemPrivate->notifiedFocus = itemPrivate->focus;
687 emit item->focusChanged(itemPrivate->focus);
690 if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) {
691 itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus;
692 itemPrivate->itemChange(QQuickItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus);
693 emit item->activeFocusChanged(itemPrivate->activeFocus);
698 void QQuickCanvasPrivate::dirtyItem(QQuickItem *)
704 void QQuickCanvasPrivate::cleanup(QSGNode *n)
708 Q_ASSERT(!cleanupNodeList.contains(n));
709 cleanupNodeList.append(n);
715 \qmlclass Window QQuickCanvas
716 \inqmlmodule QtQuick.Window 2
717 \brief The Window object creates a new top-level window.
719 The Window object creates a new top-level window for a QtQuick scene. It automatically sets up the
720 window for use with QtQuick 2.0 graphical elements.
725 \brief The QQuickCanvas class provides the canvas for displaying a graphical QML scene
727 QQuickCanvas provides the graphical scene management needed to interact with and display
728 a scene of QQuickItems.
730 A QQuickCanvas always has a single invisible root item. To add items to this canvas,
731 reparent the items to the root item or to an existing item in the scene.
733 For easily displaying a scene from a QML file, see \l{QQuickView}.
736 \section1 Scene Graph and Rendering
738 The QQuickCanvas uses a scene graph on top of OpenGL to render. This scene graph is disconnected
739 from the QML scene and potentially lives in another thread, depending on the platform
740 implementation. Since the rendering scene graph lives independently from the QML scene, it can
741 also be completely released without affecting the state of the QML scene.
743 The sceneGraphInitialized() signal is emitted on the rendering thread before the QML scene is
744 rendered to the screen for the first time. If the rendering scene graph has been released
745 the signal will be emitted again before the next frame is rendered.
747 Rendering is done by first copying the QML scene's state into the rendering scene graph. This is
748 done by calling QQuickItem::updatePaintNode() functions on all items that have changed. This phase
749 is run on the rendering thread with the GUI thread blocked, when a separate rendering thread
750 is being used. The scene can then be rendered.
752 Before the scene graph is rendered, the beforeRendering() signal is emitted. The OpenGL context
753 is bound at this point and the application is free to do its own rendering. Also
754 make sure to disable the clearing of the color buffer, using setClearBeforeRendering(). The
755 default clear color is white and can be changed with setClearColor(). After the scene has
756 been rendered, the afterRendering() signal is emitted. The application can use this to render
757 OpenGL on top of a QML application. Once the frame is fully done and has been swapped,
758 the frameSwapped() signal is emitted.
760 While the scene graph is being rendered on the rendering thread, the GUI will process animations
761 for the next frame. This means that as long as users are not using scene graph API
762 directly, the added complexity of a rendering thread can be completely ignored.
764 When a QQuickCanvas is programatically hidden with hide() or setVisible(false), it will
765 stop rendering and its scene graph and OpenGL context might be released. The
766 sceneGraphInvalidated() signal will be emitted when this happens.
768 \warning It is crucial that OpenGL operations and interaction with the scene graph happens
769 exclusively on the rendering thread, primarily during the updatePaintNode() phase.
771 \warning As signals related to rendering might be emitted from the rendering thread,
772 connections should be made using Qt::DirectConnection
775 \section1 Resource Management
777 QML will typically try to cache images, scene graph nodes, etc to improve performance, but in
778 some low-memory scenarios it might be required to aggressively release these resources. The
779 releaseResources() can be used to force clean up of certain resources. Calling releaseResources()
780 may result in the entire scene graph and its OpenGL context being deleted. The
781 sceneGraphInvalidated() signal will be emitted when this happens.
784 QQuickCanvas::QQuickCanvas(QWindow *parent)
785 : QWindow(*(new QQuickCanvasPrivate), parent)
791 QQuickCanvas::QQuickCanvas(QQuickCanvasPrivate &dd, QWindow *parent)
792 : QWindow(dd, parent)
798 QQuickCanvas::~QQuickCanvas()
802 d->windowManager->canvasDestroyed(this);
804 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
805 delete d->incubationController; d->incubationController = 0;
807 delete d->rootItem; d->rootItem = 0;
813 This function tries to release redundant resources currently held by the QML scene.
815 Calling this function might result in the scene graph and the OpenGL context used
816 for rendering being released to release graphics memory. If this happens, the
817 sceneGraphInvalidated() signal will be called, allowing users to clean up their
818 own graphics resources. The setPersistentOpenGLContext() and setPersistentSceneGraph()
819 functions can be used to prevent this from happening, if handling the cleanup is
820 not feasible in the application, at the cost of higher memory usage.
822 \sa sceneGraphInvalidated(), setPersistentOpenGLContext(), setPersistentSceneGraph().
825 void QQuickCanvas::releaseResources()
828 d->windowManager->releaseResources();
829 QQuickPixmap::purgeCache();
835 Controls whether the OpenGL context can be released as a part of a call to
838 The OpenGL context might still be released when the user makes an explicit
841 \sa setPersistentSceneGraph()
844 void QQuickCanvas::setPersistentOpenGLContext(bool persistent)
847 d->persistentGLContext = persistent;
852 Returns whether the OpenGL context can be released as a part of a call to
856 bool QQuickCanvas::isPersistentOpenGLContext() const
858 Q_D(const QQuickCanvas);
859 return d->persistentGLContext;
865 Controls whether the scene graph nodes and resources can be released as a
866 part of a call to releaseResources().
868 The scene graph nodes and resources might still be released when the user
869 makes an explicit call to hide().
871 \sa setPersistentOpenGLContext()
874 void QQuickCanvas::setPersistentSceneGraph(bool persistent)
877 d->persistentSceneGraph = persistent;
883 Returns whether the scene graph nodes and resources can be released as a part
884 of a call to releaseResources().
887 bool QQuickCanvas::isPersistentSceneGraph() const
889 Q_D(const QQuickCanvas);
890 return d->persistentSceneGraph;
898 Returns the invisible root item of the scene.
900 A QQuickCanvas always has a single invisible root item. To add items to this canvas,
901 reparent the items to the root item or to an existing item in the scene.
903 QQuickItem *QQuickCanvas::rootItem() const
905 Q_D(const QQuickCanvas);
911 Returns the item which currently has active focus.
913 QQuickItem *QQuickCanvas::activeFocusItem() const
915 Q_D(const QQuickCanvas);
917 return d->activeFocusItem;
920 QObject *QQuickCanvas::focusObject() const
922 Q_D(const QQuickCanvas);
924 if (d->activeFocusItem)
925 return d->activeFocusItem;
926 return const_cast<QQuickCanvas*>(this);
931 Returns the item which currently has the mouse grab.
933 QQuickItem *QQuickCanvas::mouseGrabberItem() const
935 Q_D(const QQuickCanvas);
937 return d->mouseGrabberItem;
942 \qmlproperty color QtQuick2.Window::Window::color
944 The background color for the window.
946 Setting this property is more efficient than using a separate Rectangle.
949 bool QQuickCanvasPrivate::clearHover()
951 if (hoverItems.isEmpty())
954 QPointF pos = QCursor::pos(); // ### refactor: q->mapFromGlobal(QCursor::pos());
956 bool accepted = false;
957 foreach (QQuickItem* item, hoverItems)
958 accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), true) || accepted;
964 bool QQuickCanvas::event(QEvent *e)
970 case QEvent::TouchBegin:
971 case QEvent::TouchUpdate:
972 case QEvent::TouchEnd:
973 case QEvent::TouchCancel:
975 QTouchEvent *touch = static_cast<QTouchEvent *>(e);
976 d->translateTouchEvent(touch);
977 d->deliverTouchEvent(touch);
978 if (qmlTranslateTouchToMouse())
979 d->translateTouchToMouse(touch);
981 return touch->isAccepted();
985 d->lastMousePosition = QPoint();
987 case QEvent::DragEnter:
988 case QEvent::DragLeave:
989 case QEvent::DragMove:
991 d->deliverDragEvent(&d->dragGrabber, e);
993 case QEvent::WindowDeactivate:
994 rootItem()->windowDeactivateEvent();
1000 return QWindow::event(e);
1004 void QQuickCanvas::keyPressEvent(QKeyEvent *e)
1008 if (d->activeFocusItem)
1009 sendEvent(d->activeFocusItem, e);
1013 void QQuickCanvas::keyReleaseEvent(QKeyEvent *e)
1017 if (d->activeFocusItem)
1018 sendEvent(d->activeFocusItem, e);
1021 bool QQuickCanvasPrivate::deliverInitialMousePressEvent(QQuickItem *item, QMouseEvent *event)
1025 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1026 if (itemPrivate->opacity() == 0.0)
1029 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1030 QPointF p = item->mapFromScene(event->windowPos());
1031 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
1035 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1036 for (int ii = children.count() - 1; ii >= 0; --ii) {
1037 QQuickItem *child = children.at(ii);
1038 if (!child->isVisible() || !child->isEnabled())
1040 if (deliverInitialMousePressEvent(child, event))
1044 if (itemPrivate->acceptedMouseButtons() & event->button()) {
1045 QPointF p = item->mapFromScene(event->windowPos());
1046 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1047 QMouseEvent me(event->type(), p, event->windowPos(), event->screenPos(),
1048 event->button(), event->buttons(), event->modifiers());
1051 q->sendEvent(item, &me);
1052 event->setAccepted(me.isAccepted());
1053 if (me.isAccepted())
1055 if (mouseGrabberItem)
1056 mouseGrabberItem->ungrabMouse();
1063 bool QQuickCanvasPrivate::deliverMouseEvent(QMouseEvent *event)
1067 lastMousePosition = event->windowPos();
1069 if (!mouseGrabberItem &&
1070 event->type() == QEvent::MouseButtonPress &&
1071 (event->buttons() & event->button()) == event->buttons()) {
1072 if (deliverInitialMousePressEvent(rootItem, event))
1076 return event->isAccepted();
1079 if (mouseGrabberItem) {
1080 QQuickItemPrivate *mgPrivate = QQuickItemPrivate::get(mouseGrabberItem);
1081 const QTransform &transform = mgPrivate->canvasToItemTransform();
1082 QQuickMouseEventEx me(event->type(), transform.map(event->windowPos()),
1083 event->windowPos(), event->screenPos(),
1084 event->button(), event->buttons(), event->modifiers());
1085 if (QQuickMouseEventEx::extended(event))
1086 me.setVelocity(QQuickMouseEventEx::extended(event)->velocity());
1088 q->sendEvent(mouseGrabberItem, &me);
1089 event->setAccepted(me.isAccepted());
1090 if (me.isAccepted())
1098 void QQuickCanvas::mousePressEvent(QMouseEvent *event)
1101 if (qmlTranslateTouchToMouse())
1102 return; // We are using touch events
1104 qWarning() << "QQuickCanvas::mousePressEvent()" << event->pos() << event->button() << event->buttons();
1107 d->deliverMouseEvent(event);
1111 void QQuickCanvas::mouseReleaseEvent(QMouseEvent *event)
1114 if (qmlTranslateTouchToMouse())
1115 return; // We are using touch events
1117 qWarning() << "QQuickCanvas::mouseReleaseEvent()" << event->pos() << event->button() << event->buttons();
1120 if (!d->mouseGrabberItem) {
1121 QWindow::mouseReleaseEvent(event);
1125 d->deliverMouseEvent(event);
1126 if (d->mouseGrabberItem)
1127 d->mouseGrabberItem->ungrabMouse();
1131 void QQuickCanvas::mouseDoubleClickEvent(QMouseEvent *event)
1134 if (qmlTranslateTouchToMouse())
1135 return; // We are using touch events
1138 qWarning() << "QQuickCanvas::mouseDoubleClickEvent()" << event->pos() << event->button() << event->buttons();
1141 if (!d->mouseGrabberItem && (event->buttons() & event->button()) == event->buttons()) {
1142 if (d->deliverInitialMousePressEvent(d->rootItem, event))
1149 d->deliverMouseEvent(event);
1152 bool QQuickCanvasPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
1153 const QPointF &scenePos, const QPointF &lastScenePos,
1154 Qt::KeyboardModifiers modifiers, bool accepted)
1157 const QTransform transform = QQuickItemPrivate::get(item)->canvasToItemTransform();
1159 //create copy of event
1160 QHoverEvent hoverEvent(type, transform.map(scenePos), transform.map(lastScenePos), modifiers);
1161 hoverEvent.setAccepted(accepted);
1163 q->sendEvent(item, &hoverEvent);
1165 return hoverEvent.isAccepted();
1169 void QQuickCanvas::mouseMoveEvent(QMouseEvent *event)
1172 if (qmlTranslateTouchToMouse())
1173 return; // We are using touch events
1175 qWarning() << "QQuickCanvas::mouseMoveEvent()" << event->pos() << event->button() << event->buttons();
1178 if (!d->mouseGrabberItem) {
1179 if (d->lastMousePosition.isNull())
1180 d->lastMousePosition = event->windowPos();
1181 QPointF last = d->lastMousePosition;
1182 d->lastMousePosition = event->windowPos();
1184 bool accepted = event->isAccepted();
1185 bool delivered = d->deliverHoverEvent(d->rootItem, event->windowPos(), last, event->modifiers(), accepted);
1187 //take care of any exits
1188 accepted = d->clearHover();
1190 event->setAccepted(accepted);
1194 d->deliverMouseEvent(event);
1197 bool QQuickCanvasPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
1198 Qt::KeyboardModifiers modifiers, bool &accepted)
1200 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1201 if (itemPrivate->opacity() == 0.0)
1204 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1205 QPointF p = item->mapFromScene(scenePos);
1206 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
1210 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1211 for (int ii = children.count() - 1; ii >= 0; --ii) {
1212 QQuickItem *child = children.at(ii);
1213 if (!child->isVisible() || !child->isEnabled())
1215 if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, accepted))
1219 if (itemPrivate->hoverEnabled) {
1220 QPointF p = item->mapFromScene(scenePos);
1221 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1222 if (!hoverItems.isEmpty() && hoverItems[0] == item) {
1224 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1226 QList<QQuickItem *> itemsToHover;
1227 QQuickItem* parent = item;
1228 itemsToHover << item;
1229 while ((parent = parent->parentItem()))
1230 itemsToHover << parent;
1232 // Leaving from previous hovered items until we reach the item or one of its ancestors.
1233 while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems[0])) {
1234 sendHoverEvent(QEvent::HoverLeave, hoverItems[0], scenePos, lastScenePos, modifiers, accepted);
1235 hoverItems.removeFirst();
1238 if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item
1239 // ### Shouldn't we send moves for the parent items as well?
1240 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1242 // Enter items that are not entered yet.
1244 if (!hoverItems.isEmpty())
1245 startIdx = itemsToHover.indexOf(hoverItems[0]) - 1;
1247 startIdx = itemsToHover.count() - 1;
1249 for (int i = startIdx; i >= 0; i--) {
1250 QQuickItem *itemToHover = itemsToHover[i];
1251 if (QQuickItemPrivate::get(itemToHover)->hoverEnabled) {
1252 hoverItems.prepend(itemToHover);
1253 sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, accepted);
1265 bool QQuickCanvasPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event)
1268 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1269 if (itemPrivate->opacity() == 0.0)
1272 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1273 QPointF p = item->mapFromScene(event->posF());
1274 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
1278 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1279 for (int ii = children.count() - 1; ii >= 0; --ii) {
1280 QQuickItem *child = children.at(ii);
1281 if (!child->isVisible() || !child->isEnabled())
1283 if (deliverWheelEvent(child, event))
1287 QPointF p = item->mapFromScene(event->posF());
1288 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1289 QWheelEvent wheel(p, p, event->pixelDelta(), event->angleDelta(), event->delta(),
1290 event->orientation(), event->buttons(), event->modifiers());
1292 q->sendEvent(item, &wheel);
1293 if (wheel.isAccepted()) {
1302 #ifndef QT_NO_WHEELEVENT
1304 void QQuickCanvas::wheelEvent(QWheelEvent *event)
1308 qWarning() << "QQuickCanvas::wheelEvent()" << event->pos() << event->delta() << event->orientation();
1311 d->deliverWheelEvent(d->rootItem, event);
1313 #endif // QT_NO_WHEELEVENT
1315 bool QQuickCanvasPrivate::deliverTouchEvent(QTouchEvent *event)
1318 if (event->type() == QEvent::TouchBegin)
1319 qWarning("touchBeginEvent");
1320 else if (event->type() == QEvent::TouchUpdate)
1321 qWarning("touchUpdateEvent");
1322 else if (event->type() == QEvent::TouchEnd)
1323 qWarning("touchEndEvent");
1324 else if (event->type() == QEvent::TouchCancel)
1325 qWarning("touchCancelEvent");
1330 QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > updatedPoints;
1332 if (event->type() == QTouchEvent::TouchBegin) { // all points are new touch points
1333 QSet<int> acceptedNewPoints;
1334 deliverTouchPoints(rootItem, event, event->touchPoints(), &acceptedNewPoints, &updatedPoints);
1335 if (acceptedNewPoints.count() > 0)
1339 return event->isAccepted();
1342 if (event->type() == QTouchEvent::TouchCancel) {
1343 // A TouchCancel event will typically not contain any points.
1344 // Deliver it to all items that have active touches.
1345 QSet<QQuickItem *> cancelDelivered;
1346 foreach (QQuickItem *item, itemForTouchPointId) {
1347 if (cancelDelivered.contains(item))
1349 cancelDelivered.insert(item);
1350 q->sendEvent(item, event);
1352 // The next touch event can only be a TouchBegin so clean up.
1353 itemForTouchPointId.clear();
1357 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
1358 QList<QTouchEvent::TouchPoint> newPoints;
1359 QQuickItem *item = 0;
1360 for (int i=0; i<touchPoints.count(); i++) {
1361 const QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
1362 switch (touchPoint.state()) {
1363 case Qt::TouchPointPressed:
1364 newPoints << touchPoint;
1366 case Qt::TouchPointMoved:
1367 case Qt::TouchPointStationary:
1368 case Qt::TouchPointReleased:
1369 if (itemForTouchPointId.contains(touchPoint.id())) {
1370 item = itemForTouchPointId[touchPoint.id()];
1372 updatedPoints[item].append(touchPoint);
1380 if (newPoints.count() > 0 || updatedPoints.count() > 0) {
1381 QSet<int> acceptedNewPoints;
1382 int prevCount = updatedPoints.count();
1383 deliverTouchPoints(rootItem, event, newPoints, &acceptedNewPoints, &updatedPoints);
1384 if (acceptedNewPoints.count() > 0 || updatedPoints.count() != prevCount)
1391 if (event->touchPointStates() & Qt::TouchPointReleased) {
1392 for (int i=0; i<touchPoints.count(); i++) {
1393 if (touchPoints[i].state() == Qt::TouchPointReleased)
1394 itemForTouchPointId.remove(touchPoints[i].id());
1398 return event->isAccepted();
1401 bool QQuickCanvasPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints, QSet<int> *acceptedNewPoints, QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *updatedPoints)
1404 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1406 if (itemPrivate->opacity() == 0.0)
1409 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1410 QRectF bounds(0, 0, item->width(), item->height());
1411 for (int i=0; i<newPoints.count(); i++) {
1412 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1413 if (!bounds.contains(p))
1418 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1419 for (int ii = children.count() - 1; ii >= 0; --ii) {
1420 QQuickItem *child = children.at(ii);
1421 if (!child->isEnabled() || !child->isVisible())
1423 if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints))
1427 QList<QTouchEvent::TouchPoint> matchingPoints;
1428 if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) {
1429 QRectF bounds(0, 0, item->width(), item->height());
1430 for (int i=0; i<newPoints.count(); i++) {
1431 if (acceptedNewPoints->contains(newPoints[i].id()))
1433 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1434 if (bounds.contains(p))
1435 matchingPoints << newPoints[i];
1439 if (matchingPoints.count() > 0 || (*updatedPoints)[item].count() > 0) {
1440 QList<QTouchEvent::TouchPoint> &eventPoints = (*updatedPoints)[item];
1441 eventPoints.append(matchingPoints);
1442 transformTouchPoints(eventPoints, itemPrivate->canvasToItemTransform());
1444 Qt::TouchPointStates eventStates;
1445 for (int i=0; i<eventPoints.count(); i++)
1446 eventStates |= eventPoints[i].state();
1447 // if all points have the same state, set the event type accordingly
1448 QEvent::Type eventType;
1449 switch (eventStates) {
1450 case Qt::TouchPointPressed:
1451 eventType = QEvent::TouchBegin;
1453 case Qt::TouchPointReleased:
1454 eventType = QEvent::TouchEnd;
1457 eventType = QEvent::TouchUpdate;
1461 if (eventStates != Qt::TouchPointStationary) {
1462 QTouchEvent touchEvent(eventType);
1463 touchEvent.setWindow(event->window());
1464 touchEvent.setTarget(item);
1465 touchEvent.setDevice(event->device());
1466 touchEvent.setModifiers(event->modifiers());
1467 touchEvent.setTouchPointStates(eventStates);
1468 touchEvent.setTouchPoints(eventPoints);
1469 touchEvent.setTimestamp(event->timestamp());
1471 touchEvent.accept();
1472 q->sendEvent(item, &touchEvent);
1474 if (touchEvent.isAccepted()) {
1475 for (int i=0; i<matchingPoints.count(); i++) {
1476 itemForTouchPointId[matchingPoints[i].id()] = item;
1477 acceptedNewPoints->insert(matchingPoints[i].id());
1483 updatedPoints->remove(item);
1484 if (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty())
1490 void QQuickCanvasPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event)
1493 grabber->resetTarget();
1494 QQuickDragGrabber::iterator grabItem = grabber->begin();
1495 if (grabItem != grabber->end()) {
1496 Q_ASSERT(event->type() != QEvent::DragEnter);
1497 if (event->type() == QEvent::Drop) {
1498 QDropEvent *e = static_cast<QDropEvent *>(event);
1499 for (e->setAccepted(false); !e->isAccepted() && grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1500 QPointF p = (**grabItem)->mapFromScene(e->pos());
1501 QDropEvent translatedEvent(
1503 e->possibleActions(),
1506 e->keyboardModifiers());
1507 QQuickDropEventEx::copyActions(&translatedEvent, *e);
1508 q->sendEvent(**grabItem, &translatedEvent);
1509 e->setAccepted(translatedEvent.isAccepted());
1510 e->setDropAction(translatedEvent.dropAction());
1511 grabber->setTarget(**grabItem);
1514 if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave.
1515 QDragLeaveEvent leaveEvent;
1516 for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
1517 q->sendEvent(**grabItem, &leaveEvent);
1519 } else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1520 QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event);
1521 if (deliverDragEvent(grabber, **grabItem, moveEvent)) {
1522 moveEvent->setAccepted(true);
1523 for (++grabItem; grabItem != grabber->end();) {
1524 QPointF p = (**grabItem)->mapFromScene(moveEvent->pos());
1525 if (QRectF(0, 0, (**grabItem)->width(), (**grabItem)->height()).contains(p)) {
1526 QDragMoveEvent translatedEvent(
1528 moveEvent->possibleActions(),
1529 moveEvent->mimeData(),
1530 moveEvent->mouseButtons(),
1531 moveEvent->keyboardModifiers());
1532 QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent);
1533 q->sendEvent(**grabItem, &translatedEvent);
1536 QDragLeaveEvent leaveEvent;
1537 q->sendEvent(**grabItem, &leaveEvent);
1538 grabItem = grabber->release(grabItem);
1543 QDragLeaveEvent leaveEvent;
1544 q->sendEvent(**grabItem, &leaveEvent);
1548 if (event->type() == QEvent::DragEnter || event->type() == QEvent::DragMove) {
1549 QDragMoveEvent *e = static_cast<QDragMoveEvent *>(event);
1550 QDragEnterEvent enterEvent(
1552 e->possibleActions(),
1555 e->keyboardModifiers());
1556 QQuickDropEventEx::copyActions(&enterEvent, *e);
1557 event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent));
1561 bool QQuickCanvasPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event)
1564 bool accepted = false;
1565 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1566 if (itemPrivate->opacity() == 0.0 || !item->isVisible() || !item->isEnabled())
1569 QPointF p = item->mapFromScene(event->pos());
1570 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1571 if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) {
1572 QDragMoveEvent translatedEvent(
1574 event->possibleActions(),
1576 event->mouseButtons(),
1577 event->keyboardModifiers(),
1579 QQuickDropEventEx::copyActions(&translatedEvent, *event);
1580 q->sendEvent(item, &translatedEvent);
1581 if (event->type() == QEvent::DragEnter) {
1582 if (translatedEvent.isAccepted()) {
1583 grabber->grab(item);
1590 } else if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1594 QDragEnterEvent enterEvent(
1596 event->possibleActions(),
1598 event->mouseButtons(),
1599 event->keyboardModifiers());
1600 QQuickDropEventEx::copyActions(&enterEvent, *event);
1601 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1602 for (int ii = children.count() - 1; ii >= 0; --ii) {
1603 if (deliverDragEvent(grabber, children.at(ii), &enterEvent))
1610 bool QQuickCanvasPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QEvent *event)
1615 QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target);
1616 if (targetPrivate->filtersChildMouseEvents)
1617 if (target->childMouseEventFilter(item, event))
1620 if (sendFilteredMouseEvent(target->parentItem(), item, event))
1627 Propagates an event to a QQuickItem on the canvas
1629 bool QQuickCanvas::sendEvent(QQuickItem *item, QEvent *e)
1634 qWarning("QQuickCanvas::sendEvent: Cannot send event to a null item");
1640 switch (e->type()) {
1641 case QEvent::KeyPress:
1642 case QEvent::KeyRelease:
1644 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1645 while (!e->isAccepted() && (item = item->parentItem())) {
1647 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1650 case QEvent::FocusIn:
1651 case QEvent::FocusOut:
1652 QQuickItemPrivate::get(item)->deliverFocusEvent(static_cast<QFocusEvent *>(e));
1654 case QEvent::MouseButtonPress:
1655 case QEvent::MouseButtonRelease:
1656 case QEvent::MouseButtonDblClick:
1657 case QEvent::MouseMove:
1658 // XXX todo - should sendEvent be doing this? how does it relate to forwarded events?
1659 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
1661 QQuickItemPrivate::get(item)->deliverMouseEvent(static_cast<QMouseEvent *>(e));
1664 case QEvent::UngrabMouse:
1665 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
1667 item->mouseUngrabEvent();
1671 QQuickItemPrivate::get(item)->deliverWheelEvent(static_cast<QWheelEvent *>(e));
1673 case QEvent::HoverEnter:
1674 case QEvent::HoverLeave:
1675 case QEvent::HoverMove:
1676 QQuickItemPrivate::get(item)->deliverHoverEvent(static_cast<QHoverEvent *>(e));
1678 case QEvent::TouchBegin:
1679 case QEvent::TouchUpdate:
1680 case QEvent::TouchEnd:
1681 case QEvent::TouchCancel:
1682 // XXX todo - should sendEvent be doing this? how does it relate to forwarded events?
1683 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
1685 QQuickItemPrivate::get(item)->deliverTouchEvent(static_cast<QTouchEvent *>(e));
1688 case QEvent::DragEnter:
1689 case QEvent::DragMove:
1690 case QEvent::DragLeave:
1692 QQuickItemPrivate::get(item)->deliverDragEvent(e);
1701 void QQuickCanvasPrivate::cleanupNodes()
1703 for (int ii = 0; ii < cleanupNodeList.count(); ++ii)
1704 delete cleanupNodeList.at(ii);
1705 cleanupNodeList.clear();
1708 void QQuickCanvasPrivate::cleanupNodesOnShutdown(QQuickItem *item)
1710 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
1711 if (p->itemNodeInstance) {
1712 delete p->itemNodeInstance;
1713 p->itemNodeInstance = 0;
1715 if (p->extra.isAllocated()) {
1716 p->extra->opacityNode = 0;
1717 p->extra->clipNode = 0;
1718 p->extra->rootNode = 0;
1725 for (int ii = 0; ii < p->childItems.count(); ++ii)
1726 cleanupNodesOnShutdown(p->childItems.at(ii));
1729 // This must be called from the render thread, with the main thread frozen
1730 void QQuickCanvasPrivate::cleanupNodesOnShutdown()
1733 cleanupNodesOnShutdown(rootItem);
1734 QSet<QQuickItem *>::const_iterator it = parentlessItems.begin();
1735 for (; it != parentlessItems.end(); ++it)
1736 cleanupNodesOnShutdown(*it);
1739 void QQuickCanvasPrivate::updateDirtyNodes()
1742 qWarning() << "QQuickCanvasPrivate::updateDirtyNodes():";
1747 QQuickItem *updateList = dirtyItemList;
1749 if (updateList) QQuickItemPrivate::get(updateList)->prevDirtyItem = &updateList;
1751 while (updateList) {
1752 QQuickItem *item = updateList;
1753 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
1754 itemPriv->removeFromDirtyList();
1757 qWarning() << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString());
1759 updateDirtyNode(item);
1763 void QQuickCanvasPrivate::updateDirtyNode(QQuickItem *item)
1765 #ifdef QML_RUNTIME_TESTING
1766 bool didFlash = false;
1769 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
1770 quint32 dirty = itemPriv->dirtyAttributes;
1771 itemPriv->dirtyAttributes = 0;
1773 if ((dirty & QQuickItemPrivate::TransformUpdateMask) ||
1774 (dirty & QQuickItemPrivate::Size && itemPriv->origin() != QQuickItem::TopLeft &&
1775 (itemPriv->scale() != 1. || itemPriv->rotation() != 0.))) {
1779 if (itemPriv->x != 0. || itemPriv->y != 0.)
1780 matrix.translate(itemPriv->x, itemPriv->y);
1782 for (int ii = itemPriv->transforms.count() - 1; ii >= 0; --ii)
1783 itemPriv->transforms.at(ii)->applyTo(&matrix);
1785 if (itemPriv->scale() != 1. || itemPriv->rotation() != 0.) {
1786 QPointF origin = item->transformOriginPoint();
1787 matrix.translate(origin.x(), origin.y());
1788 if (itemPriv->scale() != 1.)
1789 matrix.scale(itemPriv->scale(), itemPriv->scale());
1790 if (itemPriv->rotation() != 0.)
1791 matrix.rotate(itemPriv->rotation(), 0, 0, 1);
1792 matrix.translate(-origin.x(), -origin.y());
1795 itemPriv->itemNode()->setMatrix(matrix);
1798 bool clipEffectivelyChanged = (dirty & (QQuickItemPrivate::Clip | QQuickItemPrivate::Canvas)) &&
1799 ((item->clip() == false) != (itemPriv->clipNode() == 0));
1800 int effectRefCount = itemPriv->extra.isAllocated()?itemPriv->extra->effectRefCount:0;
1801 bool effectRefEffectivelyChanged = (dirty & (QQuickItemPrivate::EffectReference | QQuickItemPrivate::Canvas)) &&
1802 ((effectRefCount == 0) != (itemPriv->rootNode() == 0));
1804 if (clipEffectivelyChanged) {
1805 QSGNode *parent = itemPriv->opacityNode() ? (QSGNode *) itemPriv->opacityNode() :
1806 (QSGNode *)itemPriv->itemNode();
1807 QSGNode *child = itemPriv->rootNode() ? (QSGNode *)itemPriv->rootNode() :
1808 (QSGNode *)itemPriv->groupNode;
1811 Q_ASSERT(itemPriv->clipNode() == 0);
1812 itemPriv->extra.value().clipNode = new QQuickDefaultClipNode(item->boundingRect());
1813 itemPriv->clipNode()->update();
1816 parent->removeChildNode(child);
1817 parent->appendChildNode(itemPriv->clipNode());
1819 itemPriv->clipNode()->appendChildNode(child);
1822 Q_ASSERT(itemPriv->clipNode() != 0);
1823 parent->removeChildNode(itemPriv->clipNode());
1825 itemPriv->clipNode()->removeChildNode(child);
1826 delete itemPriv->clipNode();
1827 itemPriv->extra->clipNode = 0;
1829 parent->appendChildNode(child);
1833 if (dirty & QQuickItemPrivate::ChildrenUpdateMask)
1834 itemPriv->childContainerNode()->removeAllChildNodes();
1836 if (effectRefEffectivelyChanged) {
1837 QSGNode *parent = itemPriv->clipNode();
1839 parent = itemPriv->opacityNode();
1841 parent = itemPriv->itemNode();
1842 QSGNode *child = itemPriv->groupNode;
1844 if (itemPriv->extra.isAllocated() && itemPriv->extra->effectRefCount) {
1845 Q_ASSERT(itemPriv->rootNode() == 0);
1846 itemPriv->extra->rootNode = new QSGRootNode;
1849 parent->removeChildNode(child);
1850 parent->appendChildNode(itemPriv->rootNode());
1852 itemPriv->rootNode()->appendChildNode(child);
1854 Q_ASSERT(itemPriv->rootNode() != 0);
1855 parent->removeChildNode(itemPriv->rootNode());
1857 itemPriv->rootNode()->removeChildNode(child);
1858 delete itemPriv->rootNode();
1859 itemPriv->extra->rootNode = 0;
1861 parent->appendChildNode(child);
1865 if (dirty & QQuickItemPrivate::ChildrenUpdateMask) {
1866 QSGNode *groupNode = itemPriv->groupNode;
1868 groupNode->removeAllChildNodes();
1870 QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems();
1873 for (; ii < orderedChildren.count() && orderedChildren.at(ii)->z() < 0; ++ii) {
1874 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
1875 if (!childPrivate->explicitVisible &&
1876 (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount))
1878 if (childPrivate->itemNode()->parent())
1879 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
1881 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
1884 QSGNode *beforePaintNode = itemPriv->groupNode ? itemPriv->groupNode->lastChild() : 0;
1885 if (beforePaintNode || itemPriv->extra.isAllocated())
1886 itemPriv->extra.value().beforePaintNode = beforePaintNode;
1888 if (itemPriv->paintNode)
1889 itemPriv->childContainerNode()->appendChildNode(itemPriv->paintNode);
1891 for (; ii < orderedChildren.count(); ++ii) {
1892 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
1893 if (!childPrivate->explicitVisible &&
1894 (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount))
1896 if (childPrivate->itemNode()->parent())
1897 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
1899 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
1903 if ((dirty & QQuickItemPrivate::Size) && itemPriv->clipNode()) {
1904 itemPriv->clipNode()->setRect(item->boundingRect());
1905 itemPriv->clipNode()->update();
1908 if (dirty & (QQuickItemPrivate::OpacityValue | QQuickItemPrivate::Visible
1909 | QQuickItemPrivate::HideReference | QQuickItemPrivate::Canvas))
1911 qreal opacity = itemPriv->explicitVisible && (!itemPriv->extra.isAllocated() || itemPriv->extra->hideRefCount == 0)
1912 ? itemPriv->opacity() : qreal(0);
1914 if (opacity != 1 && !itemPriv->opacityNode()) {
1915 itemPriv->extra.value().opacityNode = new QSGOpacityNode;
1917 QSGNode *parent = itemPriv->itemNode();
1918 QSGNode *child = itemPriv->clipNode();
1920 child = itemPriv->rootNode();
1922 child = itemPriv->groupNode;
1925 parent->removeChildNode(child);
1926 parent->appendChildNode(itemPriv->opacityNode());
1928 itemPriv->opacityNode()->appendChildNode(child);
1930 if (itemPriv->opacityNode())
1931 itemPriv->opacityNode()->setOpacity(opacity);
1934 if (dirty & QQuickItemPrivate::ContentUpdateMask) {
1936 if (itemPriv->flags & QQuickItem::ItemHasContents) {
1937 updatePaintNodeData.transformNode = itemPriv->itemNode();
1938 itemPriv->paintNode = item->updatePaintNode(itemPriv->paintNode, &updatePaintNodeData);
1940 Q_ASSERT(itemPriv->paintNode == 0 ||
1941 itemPriv->paintNode->parent() == 0 ||
1942 itemPriv->paintNode->parent() == itemPriv->childContainerNode());
1944 if (itemPriv->paintNode && itemPriv->paintNode->parent() == 0) {
1945 if (itemPriv->extra.isAllocated() && itemPriv->extra->beforePaintNode)
1946 itemPriv->childContainerNode()->insertChildNodeAfter(itemPriv->paintNode, itemPriv->extra->beforePaintNode);
1948 itemPriv->childContainerNode()->prependChildNode(itemPriv->paintNode);
1950 } else if (itemPriv->paintNode) {
1951 delete itemPriv->paintNode;
1952 itemPriv->paintNode = 0;
1956 if ((dirty & (QQuickItemPrivate::PerformanceHints | QQuickItemPrivate::Canvas)) && itemPriv->groupNode) {
1957 itemPriv->groupNode->setFlag(QSGNode::ChildrenDoNotOverlap, itemPriv->childrenDoNotOverlap);
1958 itemPriv->groupNode->setFlag(QSGNode::StaticSubtreeGeometry, itemPriv->staticSubtreeGeometry);
1962 // Check consistency.
1963 const QSGNode *nodeChain[] = {
1964 itemPriv->itemNodeInstance,
1965 itemPriv->opacityNode(),
1966 itemPriv->clipNode(),
1967 itemPriv->rootNode(),
1968 itemPriv->groupNode,
1969 itemPriv->paintNode,
1974 while (ip < 5 && nodeChain[ip] == 0)
1979 while (ic < 5 && nodeChain[ic] == 0)
1981 const QSGNode *parent = nodeChain[ip];
1982 const QSGNode *child = nodeChain[ic];
1984 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 0);
1986 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 1);
1987 Q_ASSERT(child->parent() == parent);
1988 bool containsChild = false;
1989 for (QSGNode *n = parent->firstChild(); n; n = n->nextSibling())
1990 containsChild |= (n == child);
1991 Q_ASSERT(containsChild);
1997 #ifdef QML_RUNTIME_TESTING
1998 if (itemPriv->sceneGraphContext()->isFlashModeEnabled()) {
1999 QSGFlashNode *flash = new QSGFlashNode();
2000 flash->setRect(item->boundingRect());
2001 itemPriv->childContainerNode()->appendChildNode(flash);
2012 void QQuickCanvas::maybeUpdate()
2015 d->windowManager->maybeUpdate(this);
2018 void QQuickCanvas::cleanupSceneGraph()
2025 delete d->renderer->rootNode();
2032 Returns the opengl context used for rendering.
2034 If the scene graph is not ready, this function will return 0.
2036 \sa sceneGraphInitialized(), sceneGraphInvalidated()
2039 QOpenGLContext *QQuickCanvas::openglContext() const
2041 Q_D(const QQuickCanvas);
2042 if (d->context->isReady())
2043 return d->context->glContext();
2049 \fn void QSGContext::sceneGraphInitialized()
2051 This signal is emitted when the scene graph has been initialized.
2053 This signal will be emitted from the scene graph rendering thread.
2059 \fn void QSGContext::sceneGraphInvalidated()
2061 This signal is emitted when the scene graph has been invalidated.
2063 This signal implies that the opengl rendering context used
2064 has been invalidated and all user resources tied to that context
2067 This signal will be emitted from the scene graph rendering thread.
2071 Returns the QSGEngine used for this scene.
2073 The engine will only be available once the scene graph has been
2074 initialized. Register for the sceneGraphEngine() signal to get
2075 notification about this.
2080 QSGEngine *QQuickCanvas::sceneGraphEngine() const
2082 Q_D(const QQuickCanvas);
2083 qWarning("QQuickCanvas::sceneGraphEngine() is deprecated, use members of QQuickCanvas instead");
2084 if (d->context && d->context->isReady())
2092 Sets the render target for this canvas to be \a fbo.
2094 The specified fbo must be created in the context of the canvas
2095 or one that shares with it.
2098 This function can only be called from the thread doing
2102 void QQuickCanvas::setRenderTarget(QOpenGLFramebufferObject *fbo)
2105 if (d->context && d->context && QThread::currentThread() != d->context->thread()) {
2106 qWarning("QQuickCanvas::setRenderThread: Cannot set render target from outside the rendering thread");
2110 d->renderTarget = fbo;
2112 d->renderTargetId = fbo->handle();
2113 d->renderTargetSize = fbo->size();
2115 d->renderTargetId = 0;
2116 d->renderTargetSize = QSize();
2123 void QQuickCanvas::setRenderTarget(uint fboId, const QSize &size)
2126 if (d->context && d->context && QThread::currentThread() != d->context->thread()) {
2127 qWarning("QQuickCanvas::setRenderThread: Cannot set render target from outside the rendering thread");
2131 d->renderTargetId = fboId;
2132 d->renderTargetSize = size;
2134 // Unset any previously set instance...
2135 d->renderTarget = 0;
2140 Returns the FBO id of the render target when set; otherwise returns 0.
2142 uint QQuickCanvas::renderTargetId() const
2144 Q_D(const QQuickCanvas);
2145 return d->renderTargetId;
2149 Returns the size of the currently set render target; otherwise returns an enpty size.
2151 QSize QQuickCanvas::renderTargetSize() const
2153 Q_D(const QQuickCanvas);
2154 return d->renderTargetSize;
2161 Returns the render target for this canvas.
2163 The default is to render to the surface of the canvas, in which
2164 case the render target is 0.
2166 QOpenGLFramebufferObject *QQuickCanvas::renderTarget() const
2168 Q_D(const QQuickCanvas);
2169 return d->renderTarget;
2174 Grabs the contents of the framebuffer and returns it as an image.
2176 This function might not work if the view is not visible.
2178 \warning Calling this function will cause performance problems.
2180 \warning This function can only be called from the GUI thread.
2182 QImage QQuickCanvas::grabFrameBuffer()
2185 return d->windowManager->grab(this);
2189 Returns an incubation controller that splices incubation between frames
2190 for this canvas. QQuickView automatically installs this controller for you,
2191 otherwise you will need to install it yourself using \l{QQmlEngine::setIncubationController}
2193 The controller is owned by the canvas and will be destroyed when the canvas
2196 QQmlIncubationController *QQuickCanvas::incubationController() const
2198 Q_D(const QQuickCanvas);
2200 if (!d->incubationController)
2201 d->incubationController = new QQuickCanvasIncubationController(const_cast<QQuickCanvasPrivate *>(d));
2202 return d->incubationController;
2208 \enum QQuickCanvas::CreateTextureOption
2210 The CreateTextureOption enums are used to customize a texture is wrapped.
2212 \value TextureHasAlphaChannel The texture has an alpha channel and should
2213 be drawn using blending.
2215 \value TextureHasMipmaps The texture has mipmaps and can be drawn with
2218 \value TextureOwnsGLTexture The texture object owns the texture id and
2219 will delete the GL texture when the texture object is deleted.
2223 \fn void QQuickCanvas::beforeRendering()
2225 This signal is emitted before the scene starts rendering.
2227 Combined with the modes for clearing the background, this option
2228 can be used to paint using raw GL under QML content.
2230 The GL context used for rendering the scene graph will be bound
2233 \warning Since this signal is emitted from the scene graph rendering thread, the
2234 receiver should be on the scene graph thread or the connection should be Qt::DirectConnection.
2236 \warning Make very sure that a signal handler for beforeRendering leaves the GL
2237 context in the same state as it was when the signal handler was entered. Failing to
2238 do so can result in the scene not rendering properly.
2242 \fn void QQuickCanvas::afterRendering()
2244 This signal is emitted after the scene has completed rendering, before swapbuffers is called.
2246 This signal can be used to paint using raw GL on top of QML content,
2247 or to do screen scraping of the current frame buffer.
2249 The GL context used for rendering the scene graph will be bound at this point.
2251 \warning Since this signal is emitted from the scene graph rendering thread, the
2252 receiver should be on the scene graph thread or the connection should be Qt::DirectConnection.
2254 \warning Make very sure that a signal handler for afterRendering() leaves the GL
2255 context in the same state as it was when the signal handler was entered. Failing to
2256 do so can result in the scene not rendering properly.
2262 Sets weither the scene graph rendering of QML should clear the color buffer
2263 before it starts rendering to \a enbled.
2265 By disabling clearing of the color buffer, it is possible to do GL painting
2266 under the scene graph.
2268 The color buffer is cleared by default.
2270 \sa beforeRendering()
2273 void QQuickCanvas::setClearBeforeRendering(bool enabled)
2276 d->clearBeforeRendering = enabled;
2282 Returns weither clearing of the color buffer is done before rendering or not.
2285 bool QQuickCanvas::clearBeforeRendering() const
2287 Q_D(const QQuickCanvas);
2288 return d->clearBeforeRendering;
2294 Creates a new QSGTexture from the supplied \a image. If the image has an
2295 alpha channel, the corresponding texture will have an alpha channel.
2297 The caller of the function is responsible for deleting the returned texture.
2298 The actual GL texture will be deleted when the texture object is deleted.
2300 \warning This function will return 0 if the scene graph has not yet been
2303 This function can be called both from the GUI thread and the rendering thread.
2305 \sa sceneGraphInitialized()
2308 QSGTexture *QQuickCanvas::createTextureFromImage(const QImage &image) const
2310 Q_D(const QQuickCanvas);
2311 if (d->context && d->context->isReady())
2312 return d->context->createTexture(image);
2320 Creates a new QSGTexture object from an existing GL texture \a id.
2322 The caller of the function is responsible for deleting the returned texture.
2324 Use \a options to customize the texture attributes.
2326 \warning This function will return 0 if the scenegraph has not yet been
2329 \sa sceneGraphInitialized()
2331 QSGTexture *QQuickCanvas::createTextureFromId(uint id, const QSize &size, CreateTextureOptions options) const
2333 Q_D(const QQuickCanvas);
2334 if (d->context && d->context->isReady()) {
2335 QSGPlainTexture *texture = new QSGPlainTexture();
2336 texture->setTextureId(id);
2337 texture->setHasAlphaChannel(options & TextureHasAlphaChannel);
2338 texture->setHasMipmaps(options & TextureHasMipmaps);
2339 texture->setOwnsTexture(options & TextureOwnsGLTexture);
2340 texture->setTextureSize(size);
2348 Sets the color used to clear the opengl context to \a color.
2350 Setting the clear color has no effect when clearing is disabled.
2352 \sa setClearBeforeRendering()
2355 void QQuickCanvas::setClearColor(const QColor &color)
2358 if (color == d->clearColor)
2361 d->clearColor = color;
2362 emit clearColorChanged(color);
2368 Returns the color used to clear the opengl context.
2371 QColor QQuickCanvas::clearColor() const
2373 return d_func()->clearColor;
2378 #include "moc_qquickcanvas.cpp"