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()->reset();
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()->reset();
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();
1001 return QWindow::event(e);
1005 void QQuickCanvas::keyPressEvent(QKeyEvent *e)
1009 if (d->activeFocusItem)
1010 sendEvent(d->activeFocusItem, e);
1014 void QQuickCanvas::keyReleaseEvent(QKeyEvent *e)
1018 if (d->activeFocusItem)
1019 sendEvent(d->activeFocusItem, e);
1022 bool QQuickCanvasPrivate::deliverInitialMousePressEvent(QQuickItem *item, QMouseEvent *event)
1026 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1027 if (itemPrivate->opacity() == 0.0)
1030 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1031 QPointF p = item->mapFromScene(event->windowPos());
1032 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
1036 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1037 for (int ii = children.count() - 1; ii >= 0; --ii) {
1038 QQuickItem *child = children.at(ii);
1039 if (!child->isVisible() || !child->isEnabled())
1041 if (deliverInitialMousePressEvent(child, event))
1045 if (itemPrivate->acceptedMouseButtons() & event->button()) {
1046 QPointF p = item->mapFromScene(event->windowPos());
1047 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1048 QMouseEvent me(event->type(), p, event->windowPos(), event->screenPos(),
1049 event->button(), event->buttons(), event->modifiers());
1052 q->sendEvent(item, &me);
1053 event->setAccepted(me.isAccepted());
1054 if (me.isAccepted())
1056 if (mouseGrabberItem)
1057 mouseGrabberItem->ungrabMouse();
1064 bool QQuickCanvasPrivate::deliverMouseEvent(QMouseEvent *event)
1068 lastMousePosition = event->windowPos();
1070 if (!mouseGrabberItem &&
1071 event->type() == QEvent::MouseButtonPress &&
1072 (event->buttons() & event->button()) == event->buttons()) {
1073 if (deliverInitialMousePressEvent(rootItem, event))
1077 return event->isAccepted();
1080 if (mouseGrabberItem) {
1081 QQuickItemPrivate *mgPrivate = QQuickItemPrivate::get(mouseGrabberItem);
1082 const QTransform &transform = mgPrivate->canvasToItemTransform();
1083 QQuickMouseEventEx me(event->type(), transform.map(event->windowPos()),
1084 event->windowPos(), event->screenPos(),
1085 event->button(), event->buttons(), event->modifiers());
1086 if (QQuickMouseEventEx::extended(event))
1087 me.setVelocity(QQuickMouseEventEx::extended(event)->velocity());
1089 q->sendEvent(mouseGrabberItem, &me);
1090 event->setAccepted(me.isAccepted());
1091 if (me.isAccepted())
1099 void QQuickCanvas::mousePressEvent(QMouseEvent *event)
1102 if (qmlTranslateTouchToMouse())
1103 return; // We are using touch events
1105 qWarning() << "QQuickCanvas::mousePressEvent()" << event->pos() << event->button() << event->buttons();
1108 d->deliverMouseEvent(event);
1112 void QQuickCanvas::mouseReleaseEvent(QMouseEvent *event)
1115 if (qmlTranslateTouchToMouse())
1116 return; // We are using touch events
1118 qWarning() << "QQuickCanvas::mouseReleaseEvent()" << event->pos() << event->button() << event->buttons();
1121 if (!d->mouseGrabberItem) {
1122 QWindow::mouseReleaseEvent(event);
1126 d->deliverMouseEvent(event);
1127 if (d->mouseGrabberItem)
1128 d->mouseGrabberItem->ungrabMouse();
1132 void QQuickCanvas::mouseDoubleClickEvent(QMouseEvent *event)
1135 if (qmlTranslateTouchToMouse())
1136 return; // We are using touch events
1139 qWarning() << "QQuickCanvas::mouseDoubleClickEvent()" << event->pos() << event->button() << event->buttons();
1142 if (!d->mouseGrabberItem && (event->buttons() & event->button()) == event->buttons()) {
1143 if (d->deliverInitialMousePressEvent(d->rootItem, event))
1150 d->deliverMouseEvent(event);
1153 bool QQuickCanvasPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
1154 const QPointF &scenePos, const QPointF &lastScenePos,
1155 Qt::KeyboardModifiers modifiers, bool accepted)
1158 const QTransform transform = QQuickItemPrivate::get(item)->canvasToItemTransform();
1160 //create copy of event
1161 QHoverEvent hoverEvent(type, transform.map(scenePos), transform.map(lastScenePos), modifiers);
1162 hoverEvent.setAccepted(accepted);
1164 q->sendEvent(item, &hoverEvent);
1166 return hoverEvent.isAccepted();
1170 void QQuickCanvas::mouseMoveEvent(QMouseEvent *event)
1173 if (qmlTranslateTouchToMouse())
1174 return; // We are using touch events
1176 qWarning() << "QQuickCanvas::mouseMoveEvent()" << event->pos() << event->button() << event->buttons();
1179 if (!d->mouseGrabberItem) {
1180 if (d->lastMousePosition.isNull())
1181 d->lastMousePosition = event->windowPos();
1182 QPointF last = d->lastMousePosition;
1183 d->lastMousePosition = event->windowPos();
1185 bool accepted = event->isAccepted();
1186 bool delivered = d->deliverHoverEvent(d->rootItem, event->windowPos(), last, event->modifiers(), accepted);
1188 //take care of any exits
1189 accepted = d->clearHover();
1191 event->setAccepted(accepted);
1195 d->deliverMouseEvent(event);
1198 bool QQuickCanvasPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
1199 Qt::KeyboardModifiers modifiers, bool &accepted)
1201 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1202 if (itemPrivate->opacity() == 0.0)
1205 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1206 QPointF p = item->mapFromScene(scenePos);
1207 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
1211 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1212 for (int ii = children.count() - 1; ii >= 0; --ii) {
1213 QQuickItem *child = children.at(ii);
1214 if (!child->isVisible() || !child->isEnabled())
1216 if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, accepted))
1220 if (itemPrivate->hoverEnabled) {
1221 QPointF p = item->mapFromScene(scenePos);
1222 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1223 if (!hoverItems.isEmpty() && hoverItems[0] == item) {
1225 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1227 QList<QQuickItem *> itemsToHover;
1228 QQuickItem* parent = item;
1229 itemsToHover << item;
1230 while ((parent = parent->parentItem()))
1231 itemsToHover << parent;
1233 // Leaving from previous hovered items until we reach the item or one of its ancestors.
1234 while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems[0])) {
1235 sendHoverEvent(QEvent::HoverLeave, hoverItems[0], scenePos, lastScenePos, modifiers, accepted);
1236 hoverItems.removeFirst();
1239 if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item
1240 // ### Shouldn't we send moves for the parent items as well?
1241 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1243 // Enter items that are not entered yet.
1245 if (!hoverItems.isEmpty())
1246 startIdx = itemsToHover.indexOf(hoverItems[0]) - 1;
1248 startIdx = itemsToHover.count() - 1;
1250 for (int i = startIdx; i >= 0; i--) {
1251 QQuickItem *itemToHover = itemsToHover[i];
1252 if (QQuickItemPrivate::get(itemToHover)->hoverEnabled) {
1253 hoverItems.prepend(itemToHover);
1254 sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, accepted);
1266 bool QQuickCanvasPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event)
1269 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1270 if (itemPrivate->opacity() == 0.0)
1273 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1274 QPointF p = item->mapFromScene(event->posF());
1275 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
1279 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1280 for (int ii = children.count() - 1; ii >= 0; --ii) {
1281 QQuickItem *child = children.at(ii);
1282 if (!child->isVisible() || !child->isEnabled())
1284 if (deliverWheelEvent(child, event))
1288 QPointF p = item->mapFromScene(event->posF());
1289 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1290 QWheelEvent wheel(p, p, event->pixelDelta(), event->angleDelta(), event->delta(),
1291 event->orientation(), event->buttons(), event->modifiers());
1293 q->sendEvent(item, &wheel);
1294 if (wheel.isAccepted()) {
1303 #ifndef QT_NO_WHEELEVENT
1305 void QQuickCanvas::wheelEvent(QWheelEvent *event)
1309 qWarning() << "QQuickCanvas::wheelEvent()" << event->pos() << event->delta() << event->orientation();
1312 d->deliverWheelEvent(d->rootItem, event);
1314 #endif // QT_NO_WHEELEVENT
1316 bool QQuickCanvasPrivate::deliverTouchEvent(QTouchEvent *event)
1319 if (event->type() == QEvent::TouchBegin)
1320 qWarning("touchBeginEvent");
1321 else if (event->type() == QEvent::TouchUpdate)
1322 qWarning("touchUpdateEvent");
1323 else if (event->type() == QEvent::TouchEnd)
1324 qWarning("touchEndEvent");
1325 else if (event->type() == QEvent::TouchCancel)
1326 qWarning("touchCancelEvent");
1331 QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > updatedPoints;
1333 if (event->type() == QTouchEvent::TouchBegin) { // all points are new touch points
1334 QSet<int> acceptedNewPoints;
1335 deliverTouchPoints(rootItem, event, event->touchPoints(), &acceptedNewPoints, &updatedPoints);
1336 if (acceptedNewPoints.count() > 0)
1340 return event->isAccepted();
1343 if (event->type() == QTouchEvent::TouchCancel) {
1344 // A TouchCancel event will typically not contain any points.
1345 // Deliver it to all items that have active touches.
1346 QSet<QQuickItem *> cancelDelivered;
1347 foreach (QQuickItem *item, itemForTouchPointId) {
1348 if (cancelDelivered.contains(item))
1350 cancelDelivered.insert(item);
1351 q->sendEvent(item, event);
1353 // The next touch event can only be a TouchBegin so clean up.
1354 itemForTouchPointId.clear();
1358 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
1359 QList<QTouchEvent::TouchPoint> newPoints;
1360 QQuickItem *item = 0;
1361 for (int i=0; i<touchPoints.count(); i++) {
1362 const QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
1363 switch (touchPoint.state()) {
1364 case Qt::TouchPointPressed:
1365 newPoints << touchPoint;
1367 case Qt::TouchPointMoved:
1368 case Qt::TouchPointStationary:
1369 case Qt::TouchPointReleased:
1370 if (itemForTouchPointId.contains(touchPoint.id())) {
1371 item = itemForTouchPointId[touchPoint.id()];
1373 updatedPoints[item].append(touchPoint);
1381 if (newPoints.count() > 0 || updatedPoints.count() > 0) {
1382 QSet<int> acceptedNewPoints;
1383 int prevCount = updatedPoints.count();
1384 deliverTouchPoints(rootItem, event, newPoints, &acceptedNewPoints, &updatedPoints);
1385 if (acceptedNewPoints.count() > 0 || updatedPoints.count() != prevCount)
1392 if (event->touchPointStates() & Qt::TouchPointReleased) {
1393 for (int i=0; i<touchPoints.count(); i++) {
1394 if (touchPoints[i].state() == Qt::TouchPointReleased)
1395 itemForTouchPointId.remove(touchPoints[i].id());
1399 return event->isAccepted();
1402 bool QQuickCanvasPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints, QSet<int> *acceptedNewPoints, QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *updatedPoints)
1405 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1407 if (itemPrivate->opacity() == 0.0)
1410 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1411 QRectF bounds(0, 0, item->width(), item->height());
1412 for (int i=0; i<newPoints.count(); i++) {
1413 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1414 if (!bounds.contains(p))
1419 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1420 for (int ii = children.count() - 1; ii >= 0; --ii) {
1421 QQuickItem *child = children.at(ii);
1422 if (!child->isEnabled() || !child->isVisible())
1424 if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints))
1428 QList<QTouchEvent::TouchPoint> matchingPoints;
1429 if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) {
1430 QRectF bounds(0, 0, item->width(), item->height());
1431 for (int i=0; i<newPoints.count(); i++) {
1432 if (acceptedNewPoints->contains(newPoints[i].id()))
1434 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1435 if (bounds.contains(p))
1436 matchingPoints << newPoints[i];
1440 if (matchingPoints.count() > 0 || (*updatedPoints)[item].count() > 0) {
1441 QList<QTouchEvent::TouchPoint> &eventPoints = (*updatedPoints)[item];
1442 eventPoints.append(matchingPoints);
1443 transformTouchPoints(eventPoints, itemPrivate->canvasToItemTransform());
1445 Qt::TouchPointStates eventStates;
1446 for (int i=0; i<eventPoints.count(); i++)
1447 eventStates |= eventPoints[i].state();
1448 // if all points have the same state, set the event type accordingly
1449 QEvent::Type eventType;
1450 switch (eventStates) {
1451 case Qt::TouchPointPressed:
1452 eventType = QEvent::TouchBegin;
1454 case Qt::TouchPointReleased:
1455 eventType = QEvent::TouchEnd;
1458 eventType = QEvent::TouchUpdate;
1462 if (eventStates != Qt::TouchPointStationary) {
1463 QTouchEvent touchEvent(eventType);
1464 touchEvent.setWindow(event->window());
1465 touchEvent.setTarget(item);
1466 touchEvent.setDevice(event->device());
1467 touchEvent.setModifiers(event->modifiers());
1468 touchEvent.setTouchPointStates(eventStates);
1469 touchEvent.setTouchPoints(eventPoints);
1470 touchEvent.setTimestamp(event->timestamp());
1472 touchEvent.accept();
1473 q->sendEvent(item, &touchEvent);
1475 if (touchEvent.isAccepted()) {
1476 for (int i=0; i<matchingPoints.count(); i++) {
1477 itemForTouchPointId[matchingPoints[i].id()] = item;
1478 acceptedNewPoints->insert(matchingPoints[i].id());
1484 updatedPoints->remove(item);
1485 if (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty())
1491 void QQuickCanvasPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event)
1494 grabber->resetTarget();
1495 QQuickDragGrabber::iterator grabItem = grabber->begin();
1496 if (grabItem != grabber->end()) {
1497 Q_ASSERT(event->type() != QEvent::DragEnter);
1498 if (event->type() == QEvent::Drop) {
1499 QDropEvent *e = static_cast<QDropEvent *>(event);
1500 for (e->setAccepted(false); !e->isAccepted() && grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1501 QPointF p = (**grabItem)->mapFromScene(e->pos());
1502 QDropEvent translatedEvent(
1504 e->possibleActions(),
1507 e->keyboardModifiers());
1508 QQuickDropEventEx::copyActions(&translatedEvent, *e);
1509 q->sendEvent(**grabItem, &translatedEvent);
1510 e->setAccepted(translatedEvent.isAccepted());
1511 e->setDropAction(translatedEvent.dropAction());
1512 grabber->setTarget(**grabItem);
1515 if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave.
1516 QDragLeaveEvent leaveEvent;
1517 for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
1518 q->sendEvent(**grabItem, &leaveEvent);
1520 } else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1521 QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event);
1522 if (deliverDragEvent(grabber, **grabItem, moveEvent)) {
1523 moveEvent->setAccepted(true);
1524 for (++grabItem; grabItem != grabber->end();) {
1525 QPointF p = (**grabItem)->mapFromScene(moveEvent->pos());
1526 if (QRectF(0, 0, (**grabItem)->width(), (**grabItem)->height()).contains(p)) {
1527 QDragMoveEvent translatedEvent(
1529 moveEvent->possibleActions(),
1530 moveEvent->mimeData(),
1531 moveEvent->mouseButtons(),
1532 moveEvent->keyboardModifiers());
1533 QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent);
1534 q->sendEvent(**grabItem, &translatedEvent);
1537 QDragLeaveEvent leaveEvent;
1538 q->sendEvent(**grabItem, &leaveEvent);
1539 grabItem = grabber->release(grabItem);
1544 QDragLeaveEvent leaveEvent;
1545 q->sendEvent(**grabItem, &leaveEvent);
1549 if (event->type() == QEvent::DragEnter || event->type() == QEvent::DragMove) {
1550 QDragMoveEvent *e = static_cast<QDragMoveEvent *>(event);
1551 QDragEnterEvent enterEvent(
1553 e->possibleActions(),
1556 e->keyboardModifiers());
1557 QQuickDropEventEx::copyActions(&enterEvent, *e);
1558 event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent));
1562 bool QQuickCanvasPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event)
1565 bool accepted = false;
1566 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1567 if (itemPrivate->opacity() == 0.0 || !item->isVisible() || !item->isEnabled())
1570 QPointF p = item->mapFromScene(event->pos());
1571 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1572 if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) {
1573 QDragMoveEvent translatedEvent(
1575 event->possibleActions(),
1577 event->mouseButtons(),
1578 event->keyboardModifiers(),
1580 QQuickDropEventEx::copyActions(&translatedEvent, *event);
1581 q->sendEvent(item, &translatedEvent);
1582 if (event->type() == QEvent::DragEnter) {
1583 if (translatedEvent.isAccepted()) {
1584 grabber->grab(item);
1591 } else if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1595 QDragEnterEvent enterEvent(
1597 event->possibleActions(),
1599 event->mouseButtons(),
1600 event->keyboardModifiers());
1601 QQuickDropEventEx::copyActions(&enterEvent, *event);
1602 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1603 for (int ii = children.count() - 1; ii >= 0; --ii) {
1604 if (deliverDragEvent(grabber, children.at(ii), &enterEvent))
1611 bool QQuickCanvasPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QEvent *event)
1616 QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target);
1617 if (targetPrivate->filtersChildMouseEvents)
1618 if (target->childMouseEventFilter(item, event))
1621 if (sendFilteredMouseEvent(target->parentItem(), item, event))
1628 Propagates an event to a QQuickItem on the canvas
1630 bool QQuickCanvas::sendEvent(QQuickItem *item, QEvent *e)
1635 qWarning("QQuickCanvas::sendEvent: Cannot send event to a null item");
1641 switch (e->type()) {
1642 case QEvent::KeyPress:
1643 case QEvent::KeyRelease:
1645 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1646 while (!e->isAccepted() && (item = item->parentItem())) {
1648 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1651 case QEvent::FocusIn:
1652 case QEvent::FocusOut:
1653 QQuickItemPrivate::get(item)->deliverFocusEvent(static_cast<QFocusEvent *>(e));
1655 case QEvent::MouseButtonPress:
1656 case QEvent::MouseButtonRelease:
1657 case QEvent::MouseButtonDblClick:
1658 case QEvent::MouseMove:
1659 // XXX todo - should sendEvent be doing this? how does it relate to forwarded events?
1660 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
1662 QQuickItemPrivate::get(item)->deliverMouseEvent(static_cast<QMouseEvent *>(e));
1665 case QEvent::UngrabMouse:
1666 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
1668 item->mouseUngrabEvent();
1672 QQuickItemPrivate::get(item)->deliverWheelEvent(static_cast<QWheelEvent *>(e));
1674 case QEvent::HoverEnter:
1675 case QEvent::HoverLeave:
1676 case QEvent::HoverMove:
1677 QQuickItemPrivate::get(item)->deliverHoverEvent(static_cast<QHoverEvent *>(e));
1679 case QEvent::TouchBegin:
1680 case QEvent::TouchUpdate:
1681 case QEvent::TouchEnd:
1682 case QEvent::TouchCancel:
1683 // XXX todo - should sendEvent be doing this? how does it relate to forwarded events?
1684 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
1686 QQuickItemPrivate::get(item)->deliverTouchEvent(static_cast<QTouchEvent *>(e));
1689 case QEvent::DragEnter:
1690 case QEvent::DragMove:
1691 case QEvent::DragLeave:
1693 QQuickItemPrivate::get(item)->deliverDragEvent(e);
1702 void QQuickCanvasPrivate::cleanupNodes()
1704 for (int ii = 0; ii < cleanupNodeList.count(); ++ii)
1705 delete cleanupNodeList.at(ii);
1706 cleanupNodeList.clear();
1709 void QQuickCanvasPrivate::cleanupNodesOnShutdown(QQuickItem *item)
1711 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
1712 if (p->itemNodeInstance) {
1713 delete p->itemNodeInstance;
1714 p->itemNodeInstance = 0;
1716 if (p->extra.isAllocated()) {
1717 p->extra->opacityNode = 0;
1718 p->extra->clipNode = 0;
1719 p->extra->rootNode = 0;
1726 for (int ii = 0; ii < p->childItems.count(); ++ii)
1727 cleanupNodesOnShutdown(p->childItems.at(ii));
1730 // This must be called from the render thread, with the main thread frozen
1731 void QQuickCanvasPrivate::cleanupNodesOnShutdown()
1734 cleanupNodesOnShutdown(rootItem);
1735 QSet<QQuickItem *>::const_iterator it = parentlessItems.begin();
1736 for (; it != parentlessItems.end(); ++it)
1737 cleanupNodesOnShutdown(*it);
1740 void QQuickCanvasPrivate::updateDirtyNodes()
1743 qWarning() << "QQuickCanvasPrivate::updateDirtyNodes():";
1748 QQuickItem *updateList = dirtyItemList;
1750 if (updateList) QQuickItemPrivate::get(updateList)->prevDirtyItem = &updateList;
1752 while (updateList) {
1753 QQuickItem *item = updateList;
1754 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
1755 itemPriv->removeFromDirtyList();
1758 qWarning() << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString());
1760 updateDirtyNode(item);
1764 void QQuickCanvasPrivate::updateDirtyNode(QQuickItem *item)
1766 #ifdef QML_RUNTIME_TESTING
1767 bool didFlash = false;
1770 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
1771 quint32 dirty = itemPriv->dirtyAttributes;
1772 itemPriv->dirtyAttributes = 0;
1774 if ((dirty & QQuickItemPrivate::TransformUpdateMask) ||
1775 (dirty & QQuickItemPrivate::Size && itemPriv->origin() != QQuickItem::TopLeft &&
1776 (itemPriv->scale() != 1. || itemPriv->rotation() != 0.))) {
1780 if (itemPriv->x != 0. || itemPriv->y != 0.)
1781 matrix.translate(itemPriv->x, itemPriv->y);
1783 for (int ii = itemPriv->transforms.count() - 1; ii >= 0; --ii)
1784 itemPriv->transforms.at(ii)->applyTo(&matrix);
1786 if (itemPriv->scale() != 1. || itemPriv->rotation() != 0.) {
1787 QPointF origin = item->transformOriginPoint();
1788 matrix.translate(origin.x(), origin.y());
1789 if (itemPriv->scale() != 1.)
1790 matrix.scale(itemPriv->scale(), itemPriv->scale());
1791 if (itemPriv->rotation() != 0.)
1792 matrix.rotate(itemPriv->rotation(), 0, 0, 1);
1793 matrix.translate(-origin.x(), -origin.y());
1796 itemPriv->itemNode()->setMatrix(matrix);
1799 bool clipEffectivelyChanged = (dirty & (QQuickItemPrivate::Clip | QQuickItemPrivate::Canvas)) &&
1800 ((item->clip() == false) != (itemPriv->clipNode() == 0));
1801 int effectRefCount = itemPriv->extra.isAllocated()?itemPriv->extra->effectRefCount:0;
1802 bool effectRefEffectivelyChanged = (dirty & (QQuickItemPrivate::EffectReference | QQuickItemPrivate::Canvas)) &&
1803 ((effectRefCount == 0) != (itemPriv->rootNode() == 0));
1805 if (clipEffectivelyChanged) {
1806 QSGNode *parent = itemPriv->opacityNode() ? (QSGNode *) itemPriv->opacityNode() :
1807 (QSGNode *)itemPriv->itemNode();
1808 QSGNode *child = itemPriv->rootNode() ? (QSGNode *)itemPriv->rootNode() :
1809 (QSGNode *)itemPriv->groupNode;
1812 Q_ASSERT(itemPriv->clipNode() == 0);
1813 itemPriv->extra.value().clipNode = new QQuickDefaultClipNode(item->boundingRect());
1814 itemPriv->clipNode()->update();
1817 parent->removeChildNode(child);
1818 parent->appendChildNode(itemPriv->clipNode());
1820 itemPriv->clipNode()->appendChildNode(child);
1823 Q_ASSERT(itemPriv->clipNode() != 0);
1824 parent->removeChildNode(itemPriv->clipNode());
1826 itemPriv->clipNode()->removeChildNode(child);
1827 delete itemPriv->clipNode();
1828 itemPriv->extra->clipNode = 0;
1830 parent->appendChildNode(child);
1834 if (dirty & QQuickItemPrivate::ChildrenUpdateMask)
1835 itemPriv->childContainerNode()->removeAllChildNodes();
1837 if (effectRefEffectivelyChanged) {
1838 QSGNode *parent = itemPriv->clipNode();
1840 parent = itemPriv->opacityNode();
1842 parent = itemPriv->itemNode();
1843 QSGNode *child = itemPriv->groupNode;
1845 if (itemPriv->extra.isAllocated() && itemPriv->extra->effectRefCount) {
1846 Q_ASSERT(itemPriv->rootNode() == 0);
1847 itemPriv->extra->rootNode = new QSGRootNode;
1850 parent->removeChildNode(child);
1851 parent->appendChildNode(itemPriv->rootNode());
1853 itemPriv->rootNode()->appendChildNode(child);
1855 Q_ASSERT(itemPriv->rootNode() != 0);
1856 parent->removeChildNode(itemPriv->rootNode());
1858 itemPriv->rootNode()->removeChildNode(child);
1859 delete itemPriv->rootNode();
1860 itemPriv->extra->rootNode = 0;
1862 parent->appendChildNode(child);
1866 if (dirty & QQuickItemPrivate::ChildrenUpdateMask) {
1867 QSGNode *groupNode = itemPriv->groupNode;
1869 groupNode->removeAllChildNodes();
1871 QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems();
1874 for (; ii < orderedChildren.count() && orderedChildren.at(ii)->z() < 0; ++ii) {
1875 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
1876 if (!childPrivate->explicitVisible &&
1877 (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount))
1879 if (childPrivate->itemNode()->parent())
1880 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
1882 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
1885 QSGNode *beforePaintNode = itemPriv->groupNode ? itemPriv->groupNode->lastChild() : 0;
1886 if (beforePaintNode || itemPriv->extra.isAllocated())
1887 itemPriv->extra.value().beforePaintNode = beforePaintNode;
1889 if (itemPriv->paintNode)
1890 itemPriv->childContainerNode()->appendChildNode(itemPriv->paintNode);
1892 for (; ii < orderedChildren.count(); ++ii) {
1893 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
1894 if (!childPrivate->explicitVisible &&
1895 (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount))
1897 if (childPrivate->itemNode()->parent())
1898 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
1900 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
1904 if ((dirty & QQuickItemPrivate::Size) && itemPriv->clipNode()) {
1905 itemPriv->clipNode()->setRect(item->boundingRect());
1906 itemPriv->clipNode()->update();
1909 if (dirty & (QQuickItemPrivate::OpacityValue | QQuickItemPrivate::Visible
1910 | QQuickItemPrivate::HideReference | QQuickItemPrivate::Canvas))
1912 qreal opacity = itemPriv->explicitVisible && (!itemPriv->extra.isAllocated() || itemPriv->extra->hideRefCount == 0)
1913 ? itemPriv->opacity() : qreal(0);
1915 if (opacity != 1 && !itemPriv->opacityNode()) {
1916 itemPriv->extra.value().opacityNode = new QSGOpacityNode;
1918 QSGNode *parent = itemPriv->itemNode();
1919 QSGNode *child = itemPriv->clipNode();
1921 child = itemPriv->rootNode();
1923 child = itemPriv->groupNode;
1926 parent->removeChildNode(child);
1927 parent->appendChildNode(itemPriv->opacityNode());
1929 itemPriv->opacityNode()->appendChildNode(child);
1931 if (itemPriv->opacityNode())
1932 itemPriv->opacityNode()->setOpacity(opacity);
1935 if (dirty & QQuickItemPrivate::ContentUpdateMask) {
1937 if (itemPriv->flags & QQuickItem::ItemHasContents) {
1938 updatePaintNodeData.transformNode = itemPriv->itemNode();
1939 itemPriv->paintNode = item->updatePaintNode(itemPriv->paintNode, &updatePaintNodeData);
1941 Q_ASSERT(itemPriv->paintNode == 0 ||
1942 itemPriv->paintNode->parent() == 0 ||
1943 itemPriv->paintNode->parent() == itemPriv->childContainerNode());
1945 if (itemPriv->paintNode && itemPriv->paintNode->parent() == 0) {
1946 if (itemPriv->extra.isAllocated() && itemPriv->extra->beforePaintNode)
1947 itemPriv->childContainerNode()->insertChildNodeAfter(itemPriv->paintNode, itemPriv->extra->beforePaintNode);
1949 itemPriv->childContainerNode()->prependChildNode(itemPriv->paintNode);
1951 } else if (itemPriv->paintNode) {
1952 delete itemPriv->paintNode;
1953 itemPriv->paintNode = 0;
1957 if ((dirty & (QQuickItemPrivate::PerformanceHints | QQuickItemPrivate::Canvas)) && itemPriv->groupNode) {
1958 itemPriv->groupNode->setFlag(QSGNode::ChildrenDoNotOverlap, itemPriv->childrenDoNotOverlap);
1959 itemPriv->groupNode->setFlag(QSGNode::StaticSubtreeGeometry, itemPriv->staticSubtreeGeometry);
1963 // Check consistency.
1964 const QSGNode *nodeChain[] = {
1965 itemPriv->itemNodeInstance,
1966 itemPriv->opacityNode(),
1967 itemPriv->clipNode(),
1968 itemPriv->rootNode(),
1969 itemPriv->groupNode,
1970 itemPriv->paintNode,
1975 while (ip < 5 && nodeChain[ip] == 0)
1980 while (ic < 5 && nodeChain[ic] == 0)
1982 const QSGNode *parent = nodeChain[ip];
1983 const QSGNode *child = nodeChain[ic];
1985 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 0);
1987 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 1);
1988 Q_ASSERT(child->parent() == parent);
1989 bool containsChild = false;
1990 for (QSGNode *n = parent->firstChild(); n; n = n->nextSibling())
1991 containsChild |= (n == child);
1992 Q_ASSERT(containsChild);
1998 #ifdef QML_RUNTIME_TESTING
1999 if (itemPriv->sceneGraphContext()->isFlashModeEnabled()) {
2000 QSGFlashNode *flash = new QSGFlashNode();
2001 flash->setRect(item->boundingRect());
2002 itemPriv->childContainerNode()->appendChildNode(flash);
2013 void QQuickCanvas::maybeUpdate()
2016 d->windowManager->maybeUpdate(this);
2019 void QQuickCanvas::cleanupSceneGraph()
2026 delete d->renderer->rootNode();
2033 Returns the opengl context used for rendering.
2035 If the scene graph is not ready, this function will return 0.
2037 \sa sceneGraphInitialized(), sceneGraphInvalidated()
2040 QOpenGLContext *QQuickCanvas::openglContext() const
2042 Q_D(const QQuickCanvas);
2043 if (d->context->isReady())
2044 return d->context->glContext();
2050 \fn void QSGContext::sceneGraphInitialized()
2052 This signal is emitted when the scene graph has been initialized.
2054 This signal will be emitted from the scene graph rendering thread.
2060 \fn void QSGContext::sceneGraphInvalidated()
2062 This signal is emitted when the scene graph has been invalidated.
2064 This signal implies that the opengl rendering context used
2065 has been invalidated and all user resources tied to that context
2068 This signal will be emitted from the scene graph rendering thread.
2072 Returns the QSGEngine used for this scene.
2074 The engine will only be available once the scene graph has been
2075 initialized. Register for the sceneGraphEngine() signal to get
2076 notification about this.
2081 QSGEngine *QQuickCanvas::sceneGraphEngine() const
2083 Q_D(const QQuickCanvas);
2084 qWarning("QQuickCanvas::sceneGraphEngine() is deprecated, use members of QQuickCanvas instead");
2085 if (d->context && d->context->isReady())
2093 Sets the render target for this canvas to be \a fbo.
2095 The specified fbo must be created in the context of the canvas
2096 or one that shares with it.
2099 This function can only be called from the thread doing
2103 void QQuickCanvas::setRenderTarget(QOpenGLFramebufferObject *fbo)
2106 if (d->context && d->context && QThread::currentThread() != d->context->thread()) {
2107 qWarning("QQuickCanvas::setRenderThread: Cannot set render target from outside the rendering thread");
2111 d->renderTarget = fbo;
2113 d->renderTargetId = fbo->handle();
2114 d->renderTargetSize = fbo->size();
2116 d->renderTargetId = 0;
2117 d->renderTargetSize = QSize();
2124 void QQuickCanvas::setRenderTarget(uint fboId, const QSize &size)
2127 if (d->context && d->context && QThread::currentThread() != d->context->thread()) {
2128 qWarning("QQuickCanvas::setRenderThread: Cannot set render target from outside the rendering thread");
2132 d->renderTargetId = fboId;
2133 d->renderTargetSize = size;
2135 // Unset any previously set instance...
2136 d->renderTarget = 0;
2141 Returns the FBO id of the render target when set; otherwise returns 0.
2143 uint QQuickCanvas::renderTargetId() const
2145 Q_D(const QQuickCanvas);
2146 return d->renderTargetId;
2150 Returns the size of the currently set render target; otherwise returns an enpty size.
2152 QSize QQuickCanvas::renderTargetSize() const
2154 Q_D(const QQuickCanvas);
2155 return d->renderTargetSize;
2162 Returns the render target for this canvas.
2164 The default is to render to the surface of the canvas, in which
2165 case the render target is 0.
2167 QOpenGLFramebufferObject *QQuickCanvas::renderTarget() const
2169 Q_D(const QQuickCanvas);
2170 return d->renderTarget;
2175 Grabs the contents of the framebuffer and returns it as an image.
2177 This function might not work if the view is not visible.
2179 \warning Calling this function will cause performance problems.
2181 \warning This function can only be called from the GUI thread.
2183 QImage QQuickCanvas::grabFrameBuffer()
2186 return d->windowManager->grab(this);
2190 Returns an incubation controller that splices incubation between frames
2191 for this canvas. QQuickView automatically installs this controller for you,
2192 otherwise you will need to install it yourself using \l{QQmlEngine::setIncubationController}
2194 The controller is owned by the canvas and will be destroyed when the canvas
2197 QQmlIncubationController *QQuickCanvas::incubationController() const
2199 Q_D(const QQuickCanvas);
2201 if (!d->incubationController)
2202 d->incubationController = new QQuickCanvasIncubationController(const_cast<QQuickCanvasPrivate *>(d));
2203 return d->incubationController;
2209 \enum QQuickCanvas::CreateTextureOption
2211 The CreateTextureOption enums are used to customize a texture is wrapped.
2213 \value TextureHasAlphaChannel The texture has an alpha channel and should
2214 be drawn using blending.
2216 \value TextureHasMipmaps The texture has mipmaps and can be drawn with
2219 \value TextureOwnsGLTexture The texture object owns the texture id and
2220 will delete the GL texture when the texture object is deleted.
2224 \fn void QQuickCanvas::beforeRendering()
2226 This signal is emitted before the scene starts rendering.
2228 Combined with the modes for clearing the background, this option
2229 can be used to paint using raw GL under QML content.
2231 The GL context used for rendering the scene graph will be bound
2234 \warning Since this signal is emitted from the scene graph rendering thread, the
2235 receiver should be on the scene graph thread or the connection should be Qt::DirectConnection.
2237 \warning Make very sure that a signal handler for beforeRendering leaves the GL
2238 context in the same state as it was when the signal handler was entered. Failing to
2239 do so can result in the scene not rendering properly.
2243 \fn void QQuickCanvas::afterRendering()
2245 This signal is emitted after the scene has completed rendering, before swapbuffers is called.
2247 This signal can be used to paint using raw GL on top of QML content,
2248 or to do screen scraping of the current frame buffer.
2250 The GL context used for rendering the scene graph will be bound at this point.
2252 \warning Since this signal is emitted from the scene graph rendering thread, the
2253 receiver should be on the scene graph thread or the connection should be Qt::DirectConnection.
2255 \warning Make very sure that a signal handler for afterRendering() leaves the GL
2256 context in the same state as it was when the signal handler was entered. Failing to
2257 do so can result in the scene not rendering properly.
2263 Sets weither the scene graph rendering of QML should clear the color buffer
2264 before it starts rendering to \a enbled.
2266 By disabling clearing of the color buffer, it is possible to do GL painting
2267 under the scene graph.
2269 The color buffer is cleared by default.
2271 \sa beforeRendering()
2274 void QQuickCanvas::setClearBeforeRendering(bool enabled)
2277 d->clearBeforeRendering = enabled;
2283 Returns weither clearing of the color buffer is done before rendering or not.
2286 bool QQuickCanvas::clearBeforeRendering() const
2288 Q_D(const QQuickCanvas);
2289 return d->clearBeforeRendering;
2295 Creates a new QSGTexture from the supplied \a image. If the image has an
2296 alpha channel, the corresponding texture will have an alpha channel.
2298 The caller of the function is responsible for deleting the returned texture.
2299 The actual GL texture will be deleted when the texture object is deleted.
2301 \warning This function will return 0 if the scene graph has not yet been
2304 This function can be called both from the GUI thread and the rendering thread.
2306 \sa sceneGraphInitialized()
2309 QSGTexture *QQuickCanvas::createTextureFromImage(const QImage &image) const
2311 Q_D(const QQuickCanvas);
2312 if (d->context && d->context->isReady())
2313 return d->context->createTexture(image);
2321 Creates a new QSGTexture object from an existing GL texture \a id.
2323 The caller of the function is responsible for deleting the returned texture.
2325 Use \a options to customize the texture attributes.
2327 \warning This function will return 0 if the scenegraph has not yet been
2330 \sa sceneGraphInitialized()
2332 QSGTexture *QQuickCanvas::createTextureFromId(uint id, const QSize &size, CreateTextureOptions options) const
2334 Q_D(const QQuickCanvas);
2335 if (d->context && d->context->isReady()) {
2336 QSGPlainTexture *texture = new QSGPlainTexture();
2337 texture->setTextureId(id);
2338 texture->setHasAlphaChannel(options & TextureHasAlphaChannel);
2339 texture->setHasMipmaps(options & TextureHasMipmaps);
2340 texture->setOwnsTexture(options & TextureOwnsGLTexture);
2341 texture->setTextureSize(size);
2349 Sets the color used to clear the opengl context to \a color.
2351 Setting the clear color has no effect when clearing is disabled.
2353 \sa setClearBeforeRendering()
2356 void QQuickCanvas::setClearColor(const QColor &color)
2359 if (color == d->clearColor)
2362 d->clearColor = color;
2363 emit clearColorChanged(color);
2369 Returns the color used to clear the opengl context.
2372 QColor QQuickCanvas::clearColor() const
2374 return d_func()->clearColor;
2379 #include "moc_qquickcanvas.cpp"