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>
53 #include <private/qquickwindowmanager_p.h>
55 #include <private/qguiapplication_p.h>
56 #include <QtGui/QInputMethod>
58 #include <private/qabstractanimation_p.h>
60 #include <QtGui/qpainter.h>
61 #include <QtGui/qevent.h>
62 #include <QtGui/qmatrix4x4.h>
63 #include <QtGui/qstylehints.h>
64 #include <QtCore/qvarlengtharray.h>
65 #include <QtCore/qabstractanimation.h>
66 #include <QtQml/qqmlincubator.h>
68 #include <QtQuick/private/qquickpixmapcache_p.h>
70 #include <private/qqmlprofilerservice_p.h>
71 #include <private/qqmlmemoryprofiler_p.h>
75 void QQuickCanvasPrivate::updateFocusItemTransform()
78 QQuickItem *focus = q->activeFocusItem();
79 if (focus && qApp->focusObject() == focus)
80 qApp->inputMethod()->setInputItemTransform(QQuickItemPrivate::get(focus)->itemToCanvasTransform());
83 class QQuickCanvasIncubationController : public QObject, public QQmlIncubationController
86 QQuickCanvasIncubationController(QQuickCanvasPrivate *canvas)
87 : m_canvas(canvas), m_eventSent(false) {}
90 virtual bool event(QEvent *e)
92 if (e->type() == QEvent::User) {
93 Q_ASSERT(m_eventSent);
94 volatile bool *amtp = m_canvas->windowManager->allowMainThreadProcessing();
95 while (incubatingObjectCount()) {
97 incubateWhile(amtp, 2);
100 QCoreApplication::processEvents();
105 return QObject::event(e);
108 virtual void incubatingObjectCountChanged(int count)
110 if (count && !m_eventSent) {
112 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
114 // If no animations are running, the renderer may be waiting
115 m_canvas->windowManager->wakeup();
119 QQuickCanvasPrivate *m_canvas;
123 #ifndef QT_NO_ACCESSIBILITY
124 QAccessibleInterface *QQuickCanvas::accessibleRoot() const
126 return QAccessible::queryAccessibleInterface(const_cast<QQuickCanvas*>(this));
135 Prior to being added to a valid canvas items can set and clear focus with no
136 effect. Only once items are added to a canvas (by way of having a parent set that
137 already belongs to a canvas) do the focus rules apply. Focus goes back to
138 having no effect if an item is removed from a canvas.
140 When an item is moved into a new focus scope (either being added to a canvas
141 for the first time, or having its parent changed), if the focus scope already has
142 a scope focused item that takes precedence over the item being added. Otherwise,
143 the focus of the added tree is used. In the case of of a tree of items being
144 added to a canvas for the first time, which may have a conflicted focus state (two
145 or more items in one scope having focus set), the same rule is applied item by item -
146 thus the first item that has focus will get it (assuming the scope doesn't already
147 have a scope focused item), and the other items will have their focus cleared.
151 // #define FOCUS_DEBUG
152 // #define MOUSE_DEBUG
153 // #define TOUCH_DEBUG
154 // #define DIRTY_DEBUG
157 void printFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1);
160 QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData()
165 QQuickRootItem::QQuickRootItem()
170 void QQuickCanvas::exposeEvent(QExposeEvent *)
173 d->windowManager->exposureChanged(this);
177 void QQuickCanvas::resizeEvent(QResizeEvent *)
180 d->windowManager->resize(this, size());
184 void QQuickCanvas::showEvent(QShowEvent *)
186 d_func()->windowManager->show(this);
190 void QQuickCanvas::hideEvent(QHideEvent *)
192 d_func()->windowManager->hide(this);
196 void QQuickCanvas::focusOutEvent(QFocusEvent *)
199 d->rootItem->setFocus(false);
203 void QQuickCanvas::focusInEvent(QFocusEvent *)
206 d->rootItem->setFocus(true);
207 d->updateFocusItemTransform();
211 void QQuickCanvasPrivate::polishItems()
213 int maxPolishCycles = 100000;
215 while (!itemsToPolish.isEmpty() && --maxPolishCycles > 0) {
216 QSet<QQuickItem *> itms = itemsToPolish;
217 itemsToPolish.clear();
219 for (QSet<QQuickItem *>::iterator it = itms.begin(); it != itms.end(); ++it) {
220 QQuickItem *item = *it;
221 QQuickItemPrivate::get(item)->polishScheduled = false;
222 item->updatePolish();
226 if (maxPolishCycles == 0)
227 qWarning("QQuickCanvas: possible QQuickItem::polish() loop");
229 updateFocusItemTransform();
233 * This parameter enables that this canvas can be rendered without
234 * being shown on screen. This feature is very limited in what it supports.
236 * There needs to be another window actually showing that we can make current
237 * to get a surface to make current AND for this feature to be useful
238 * one needs to hook into beforeRender() and set the render tareget.
241 void QQuickCanvasPrivate::setRenderWithoutShowing(bool render)
243 if (render == renderWithoutShowing)
247 renderWithoutShowing = render;
250 windowManager->show(q);
252 windowManager->hide(q);
257 * Schedules the canvas to render another frame.
259 * Calling QQuickCanvas::update() differs from QQuickItem::update() in that
260 * it always triggers a repaint, regardless of changes in the underlying
261 * scene graph or not.
263 void QQuickCanvas::update()
266 d->windowManager->update(this);
269 void forceUpdate(QQuickItem *item)
271 if (item->flags() & QQuickItem::ItemHasContents)
273 QQuickItemPrivate::get(item)->dirty(QQuickItemPrivate::ChildrenUpdateMask);
275 QList <QQuickItem *> items = item->childItems();
276 for (int i=0; i<items.size(); ++i)
277 forceUpdate(items.at(i));
280 void QQuickCanvasPrivate::syncSceneGraph()
282 QML_MEMORY_SCOPE_STRING("SceneGraph");
285 emit q->beforeSynchronizing();
287 forceUpdate(rootItem);
289 QSGRootNode *rootNode = new QSGRootNode;
290 rootNode->appendChildNode(QQuickItemPrivate::get(rootItem)->itemNode());
291 renderer = context->createRenderer();
292 renderer->setRootNode(rootNode);
297 // Copy the current state of clearing from canvas into renderer.
298 renderer->setClearColor(clearColor);
299 QSGRenderer::ClearMode mode = QSGRenderer::ClearStencilBuffer | QSGRenderer::ClearDepthBuffer;
300 if (clearBeforeRendering)
301 mode |= QSGRenderer::ClearColorBuffer;
302 renderer->setClearMode(mode);
306 void QQuickCanvasPrivate::renderSceneGraph(const QSize &size)
308 QML_MEMORY_SCOPE_STRING("SceneGraph");
310 emit q->beforeRendering();
312 renderer->setDeviceRect(QRect(QPoint(0, 0), size));
313 if (renderTargetId) {
314 fboId = renderTargetId;
315 renderer->setViewportRect(QRect(QPoint(0, 0), renderTargetSize));
317 renderer->setViewportRect(QRect(QPoint(0, 0), size));
319 renderer->setProjectionMatrixToDeviceRect();
321 context->renderNextFrame(renderer, fboId);
322 emit q->afterRendering();
325 QQuickCanvasPrivate::QQuickCanvasPrivate()
328 , mouseGrabberItem(0)
330 , touchMousePressTimestamp(0)
331 , renderWithoutShowing(false)
336 , clearColor(Qt::white)
337 , clearBeforeRendering(true)
338 , persistentGLContext(false)
339 , persistentSceneGraph(false)
340 , lastWheelEventAccepted(false)
343 , incubationController(0)
347 QQuickCanvasPrivate::~QQuickCanvasPrivate()
351 void QQuickCanvasPrivate::init(QQuickCanvas *c)
357 rootItem = new QQuickRootItem;
358 QQmlEngine::setObjectOwnership(rootItem, QQmlEngine::CppOwnership);
359 QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(rootItem);
360 rootItemPrivate->canvas = q;
361 rootItemPrivate->canvasRefCount = 1;
362 rootItemPrivate->flags |= QQuickItem::ItemIsFocusScope;
364 // In the absence of a focus in event on some platforms assume the window will
365 // be activated immediately and set focus on the rootItem
366 // ### Remove when QTBUG-22415 is resolved.
367 //It is important that this call happens after the rootItem has a canvas..
368 rootItem->setFocus(true);
370 windowManager = QQuickWindowManager::instance();
371 context = windowManager->sceneGraphContext();
372 q->setSurfaceType(QWindow::OpenGLSurface);
373 q->setFormat(context->defaultSurfaceFormat());
375 QObject::connect(context, SIGNAL(initialized()), q, SIGNAL(sceneGraphInitialized()), Qt::DirectConnection);
376 QObject::connect(context, SIGNAL(invalidated()), q, SIGNAL(sceneGraphInvalidated()), Qt::DirectConnection);
377 QObject::connect(context, SIGNAL(invalidated()), q, SLOT(cleanupSceneGraph()), Qt::DirectConnection);
380 QQmlListProperty<QObject> QQuickCanvasPrivate::data()
383 return QQuickItemPrivate::get(rootItem)->data();
386 void QQuickCanvasPrivate::initRootItem()
389 q->connect(q, SIGNAL(widthChanged(int)),
390 rootItem, SLOT(setWidth(int)));
391 q->connect(q, SIGNAL(heightChanged(int)),
392 rootItem, SLOT(setHeight(int)));
393 rootItem->setWidth(q->width());
394 rootItem->setHeight(q->height());
397 static QMouseEvent *touchToMouseEvent(QEvent::Type type, const QTouchEvent::TouchPoint &p, QTouchEvent *event, QQuickItem *item, bool transformNeeded = true)
399 // The touch point local position and velocity are not yet transformed.
400 QMouseEvent *me = new QMouseEvent(type, transformNeeded ? item->mapFromScene(p.scenePos()) : p.pos(), p.scenePos(), p.screenPos(),
401 Qt::LeftButton, Qt::LeftButton, event->modifiers());
402 me->setAccepted(true);
403 me->setTimestamp(event->timestamp());
404 QVector2D transformedVelocity = p.velocity();
405 if (transformNeeded) {
406 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
407 QMatrix4x4 transformMatrix(itemPrivate->canvasToItemTransform());
408 transformedVelocity = transformMatrix.mapVector(p.velocity()).toVector2D();
410 QGuiApplicationPrivate::setMouseEventCapsAndVelocity(me, event->device()->capabilities(), transformedVelocity);
414 bool QQuickCanvasPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *event)
417 // For each point, check if it is accepted, if not, try the next point.
418 // Any of the fingers can become the mouse one.
419 // This can happen because a mouse area might not accept an event at some point but another.
420 for (int i = 0; i < event->touchPoints().count(); ++i) {
421 const QTouchEvent::TouchPoint &p = event->touchPoints().at(i);
423 if (touchMouseId == -1 && p.state() & Qt::TouchPointPressed) {
424 QPointF pos = item->mapFromScene(p.scenePos());
426 // probably redundant, we check bounds in the calling function (matchingNewPoints)
427 if (!item->contains(pos))
430 bool doubleClick = event->timestamp() - touchMousePressTimestamp
431 < static_cast<ulong>(qApp->styleHints()->mouseDoubleClickInterval());
432 touchMousePressTimestamp = event->timestamp();
433 // Store the id already here and restore it to -1 if the event does not get
434 // accepted. Cannot defer setting the new value because otherwise if the event
435 // handler spins the event loop all subsequent moves and releases get lost.
436 touchMouseId = p.id();
437 itemForTouchPointId[touchMouseId] = item;
438 QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event, item));
440 // Send a single press and see if that's accepted
441 if (!mouseGrabberItem)
443 item->grabTouchPoints(QVector<int>() << touchMouseId);
445 q->sendEvent(item, mousePress.data());
446 event->setAccepted(mousePress->isAccepted());
447 if (!mousePress->isAccepted()) {
449 if (itemForTouchPointId.value(p.id()) == item)
450 itemForTouchPointId.remove(p.id());
452 if (mouseGrabberItem == item)
456 if (doubleClick && mousePress->isAccepted()) {
457 touchMousePressTimestamp = 0;
458 QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item));
459 q->sendEvent(item, mouseDoubleClick.data());
460 event->setAccepted(mouseDoubleClick->isAccepted());
461 if (mouseDoubleClick->isAccepted()) {
467 // The event was accepted, we are done.
468 if (mousePress->isAccepted())
470 // The event was not accepted but touchMouseId was set.
471 if (touchMouseId != -1)
473 // try the next point
475 // Touch point was there before and moved
476 } else if (p.id() == touchMouseId) {
477 if (p.state() & Qt::TouchPointMoved) {
478 if (mouseGrabberItem) {
479 QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, mouseGrabberItem));
480 q->sendEvent(mouseGrabberItem, me.data());
481 event->setAccepted(me->isAccepted());
482 if (me->isAccepted()) {
483 itemForTouchPointId[p.id()] = mouseGrabberItem; // N.B. the mouseGrabberItem may be different after returning from sendEvent()
487 // no grabber, check if we care about mouse hover
488 // FIXME: this should only happen once, not recursively... I'll ignore it just ignore hover now.
489 // hover for touch???
490 QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, item));
491 if (lastMousePosition.isNull())
492 lastMousePosition = me->windowPos();
493 QPointF last = lastMousePosition;
494 lastMousePosition = me->windowPos();
496 bool accepted = me->isAccepted();
497 bool delivered = deliverHoverEvent(rootItem, me->windowPos(), last, me->modifiers(), accepted);
499 //take care of any exits
500 accepted = clearHover();
502 me->setAccepted(accepted);
505 } else if (p.state() & Qt::TouchPointReleased) {
506 // currently handled point was released
508 if (mouseGrabberItem) {
509 QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseButtonRelease, p, event, mouseGrabberItem));
510 q->sendEvent(mouseGrabberItem, me.data());
511 if (mouseGrabberItem) // might have ungrabbed due to event
512 mouseGrabberItem->ungrabMouse();
513 return me->isAccepted();
522 void QQuickCanvasPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform)
524 QMatrix4x4 transformMatrix(transform);
525 for (int i=0; i<touchPoints.count(); i++) {
526 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
527 touchPoint.setRect(transform.mapRect(touchPoint.sceneRect()));
528 touchPoint.setStartPos(transform.map(touchPoint.startScenePos()));
529 touchPoint.setLastPos(transform.map(touchPoint.lastScenePos()));
530 touchPoint.setVelocity(transformMatrix.mapVector(touchPoint.velocity()).toVector2D());
536 Translates the data in \a touchEvent to this canvas. This method leaves the item local positions in
537 \a touchEvent untouched (these are filled in later).
539 void QQuickCanvasPrivate::translateTouchEvent(QTouchEvent *touchEvent)
541 QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
542 for (int i = 0; i < touchPoints.count(); ++i) {
543 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
545 touchPoint.setScreenRect(touchPoint.sceneRect());
546 touchPoint.setStartScreenPos(touchPoint.startScenePos());
547 touchPoint.setLastScreenPos(touchPoint.lastScenePos());
549 touchPoint.setSceneRect(touchPoint.rect());
550 touchPoint.setStartScenePos(touchPoint.startPos());
551 touchPoint.setLastScenePos(touchPoint.lastPos());
554 lastMousePosition = touchPoint.pos().toPoint();
556 touchEvent->setTouchPoints(touchPoints);
559 void QQuickCanvasPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
564 Q_ASSERT(scope || item == rootItem);
567 qWarning() << "QQuickCanvasPrivate::setFocusInScope():";
568 qWarning() << " scope:" << (QObject *)scope;
570 qWarning() << " scopeSubFocusItem:" << (QObject *)QQuickItemPrivate::get(scope)->subFocusItem;
571 qWarning() << " item:" << (QObject *)item;
572 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
575 QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0;
576 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
578 QQuickItem *oldActiveFocusItem = 0;
579 QQuickItem *newActiveFocusItem = 0;
581 QVarLengthArray<QQuickItem *, 20> changed;
583 // Does this change the active focus?
584 if (item == rootItem || (scopePrivate->activeFocus && item->isEnabled())) {
585 oldActiveFocusItem = activeFocusItem;
586 newActiveFocusItem = item;
587 while (newActiveFocusItem->isFocusScope()
588 && newActiveFocusItem->scopedFocusItem()
589 && newActiveFocusItem->scopedFocusItem()->isEnabled()) {
590 newActiveFocusItem = newActiveFocusItem->scopedFocusItem();
593 if (oldActiveFocusItem) {
595 qApp->inputMethod()->commit();
599 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
600 q->sendEvent(oldActiveFocusItem, &event);
602 QQuickItem *afi = oldActiveFocusItem;
603 while (afi && afi != scope) {
604 if (QQuickItemPrivate::get(afi)->activeFocus) {
605 QQuickItemPrivate::get(afi)->activeFocus = false;
608 afi = afi->parentItem();
613 if (item != rootItem && !(options & DontChangeSubFocusItem)) {
614 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
615 if (oldSubFocusItem) {
616 QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
617 changed << oldSubFocusItem;
620 QQuickItemPrivate::get(item)->updateSubFocusItem(scope, true);
623 if (!(options & DontChangeFocusProperty)) {
624 // if (item != rootItem || QGuiApplication::focusWindow() == q) { // QTBUG-22415
625 itemPrivate->focus = true;
630 if (newActiveFocusItem && rootItem->hasFocus()) {
631 activeFocusItem = newActiveFocusItem;
633 QQuickItemPrivate::get(newActiveFocusItem)->activeFocus = true;
634 changed << newActiveFocusItem;
636 QQuickItem *afi = newActiveFocusItem->parentItem();
637 while (afi && afi != scope) {
638 if (afi->isFocusScope()) {
639 QQuickItemPrivate::get(afi)->activeFocus = true;
642 afi = afi->parentItem();
645 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
646 q->sendEvent(newActiveFocusItem, &event);
649 emit q->focusObjectChanged(activeFocusItem);
651 if (!changed.isEmpty())
652 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
655 void QQuickCanvasPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
660 Q_ASSERT(scope || item == rootItem);
663 qWarning() << "QQuickCanvasPrivate::clearFocusInScope():";
664 qWarning() << " scope:" << (QObject *)scope;
665 qWarning() << " item:" << (QObject *)item;
666 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
669 QQuickItemPrivate *scopePrivate = 0;
671 scopePrivate = QQuickItemPrivate::get(scope);
672 if ( !scopePrivate->subFocusItem )
673 return;//No focus, nothing to do.
676 QQuickItem *oldActiveFocusItem = 0;
677 QQuickItem *newActiveFocusItem = 0;
679 QVarLengthArray<QQuickItem *, 20> changed;
681 Q_ASSERT(item == rootItem || item == scopePrivate->subFocusItem);
683 // Does this change the active focus?
684 if (item == rootItem || scopePrivate->activeFocus) {
685 oldActiveFocusItem = activeFocusItem;
686 newActiveFocusItem = scope;
688 Q_ASSERT(oldActiveFocusItem);
691 qApp->inputMethod()->commit();
695 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
696 q->sendEvent(oldActiveFocusItem, &event);
698 QQuickItem *afi = oldActiveFocusItem;
699 while (afi && afi != scope) {
700 if (QQuickItemPrivate::get(afi)->activeFocus) {
701 QQuickItemPrivate::get(afi)->activeFocus = false;
704 afi = afi->parentItem();
708 if (item != rootItem && !(options & DontChangeSubFocusItem)) {
709 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
710 if (oldSubFocusItem && !(options & DontChangeFocusProperty)) {
711 QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
712 changed << oldSubFocusItem;
715 QQuickItemPrivate::get(item)->updateSubFocusItem(scope, false);
717 } else if (!(options & DontChangeFocusProperty)) {
718 QQuickItemPrivate::get(item)->focus = false;
722 if (newActiveFocusItem) {
723 Q_ASSERT(newActiveFocusItem == scope);
724 activeFocusItem = scope;
726 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
727 q->sendEvent(newActiveFocusItem, &event);
730 emit q->focusObjectChanged(activeFocusItem);
732 if (!changed.isEmpty())
733 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
736 void QQuickCanvasPrivate::notifyFocusChangesRecur(QQuickItem **items, int remaining)
738 QQmlGuard<QQuickItem> item(*items);
741 notifyFocusChangesRecur(items + 1, remaining - 1);
744 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
746 if (itemPrivate->notifiedFocus != itemPrivate->focus) {
747 itemPrivate->notifiedFocus = itemPrivate->focus;
748 emit item->focusChanged(itemPrivate->focus);
751 if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) {
752 itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus;
753 itemPrivate->itemChange(QQuickItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus);
754 emit item->activeFocusChanged(itemPrivate->activeFocus);
759 void QQuickCanvasPrivate::dirtyItem(QQuickItem *)
765 void QQuickCanvasPrivate::cleanup(QSGNode *n)
769 Q_ASSERT(!cleanupNodeList.contains(n));
770 cleanupNodeList.append(n);
776 \qmlmodule QtQuick.Window 2
777 \title QML Module QtQuick.Window 2.0
778 \brief Contains types for window management
780 This QML module contains types for creating top-level windows and accessing screen information.
782 To use the types in this module, import the module with the following line:
785 import QtQuick.Window 2.0
790 \qmlclass Window QQuickCanvas
791 \inqmlmodule QtQuick.Window 2
792 \ingroup qtquick-visual
793 \brief Creates a new top-level window
795 The Window object creates a new top-level window for a QtQuick scene. It automatically sets up the
796 window for use with QtQuick 2.0 graphical types.
798 To use this type, you will need to import the module with the following line:
800 import QtQuick.Window 2.0
803 Restricting this import will allow you to have a QML environment without access to window system features.
811 \brief The QQuickCanvas class provides the canvas for displaying a graphical QML scene
813 QQuickCanvas provides the graphical scene management needed to interact with and display
814 a scene of QQuickItems.
816 A QQuickCanvas always has a single invisible root item. To add items to this canvas,
817 reparent the items to the root item or to an existing item in the scene.
819 For easily displaying a scene from a QML file, see \l{QQuickView}.
821 \section1 Scene Graph and Rendering
823 The QQuickCanvas uses a scene graph on top of OpenGL to render. This scene graph is disconnected
824 from the QML scene and potentially lives in another thread, depending on the platform
825 implementation. Since the rendering scene graph lives independently from the QML scene, it can
826 also be completely released without affecting the state of the QML scene.
828 The sceneGraphInitialized() signal is emitted on the rendering thread before the QML scene is
829 rendered to the screen for the first time. If the rendering scene graph has been released
830 the signal will be emitted again before the next frame is rendered.
832 The rendering of each frame is broken down into the following
833 steps, in the given order:
837 \li The QQuickCanvas::beforeSynchronizing() signal is emitted.
838 Applications can make direct connections (Qt::DirectConnection)
839 to this signal to do any preparation required before calls to
840 QQuickItem::updatePaintNode().
842 \li Synchronization of the QML state into the scene graph. This is
843 done by calling the QQuickItem::updatePaintNode() function on all
844 items that have changed since the previous frame. When a dedicated
845 rendering thread is used, the GUI thread is blocked during this
846 synchroniation. This is the only time the QML items and the nodes
847 in the scene graph interact.
849 \li The canvas to be rendered is made current using
850 QOpenGLContext::makeCurrent().
852 \li The QQuickCanvas::beforeRendering() signal is
853 emitted. Applications can make direct connections
854 (Qt::DirectConnection) to this signal to use custom OpenGL calls
855 which will then stack visually beneath the QML scene.
857 \li Items that have specified QSGNode::UsesPreprocess, will have their
858 QSGNode::preprocess() function invoked.
860 \li The QQuickCanvas is cleared according to what is specified
861 using QQuickCanvas::setClearBeforeRendering() and
862 QQuickCanvas::setClearColor().
864 \li The scene graph is rendered.
866 \li The QQuickCanvas::afterRendering() signal is
867 emitted. Applications can make direct connections
868 (Qt::DirectConnection) to this signal to use custom OpenGL calls
869 which will then stack visually over the QML scene.
871 \li The rendered frame is swapped and QQuickCanvas::frameSwapped()
876 All of the above happen on the rendering thread, when applicable.
878 While the scene graph is being rendered on the rendering thread, the GUI will process animations
879 for the next frame. This means that as long as users are not using scene graph API
880 directly, the added complexity of a rendering thread can be completely ignored.
882 When a QQuickCanvas is programatically hidden with hide() or setVisible(false), it will
883 stop rendering and its scene graph and OpenGL context might be released. The
884 sceneGraphInvalidated() signal will be emitted when this happens.
886 \warning It is crucial that OpenGL operations and interaction with the scene graph happens
887 exclusively on the rendering thread, primarily during the updatePaintNode() phase.
889 \warning As signals related to rendering might be emitted from the rendering thread,
890 connections should be made using Qt::DirectConnection
893 \section2 Resource Management
895 QML will typically try to cache images, scene graph nodes, etc to improve performance, but in
896 some low-memory scenarios it might be required to aggressively release these resources. The
897 releaseResources() can be used to force clean up of certain resources. Calling releaseResources()
898 may result in the entire scene graph and its OpenGL context being deleted. The
899 sceneGraphInvalidated() signal will be emitted when this happens.
902 QQuickCanvas::QQuickCanvas(QWindow *parent)
903 : QWindow(*(new QQuickCanvasPrivate), parent)
909 QQuickCanvas::QQuickCanvas(QQuickCanvasPrivate &dd, QWindow *parent)
910 : QWindow(dd, parent)
916 QQuickCanvas::~QQuickCanvas()
920 d->windowManager->canvasDestroyed(this);
922 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
923 delete d->incubationController; d->incubationController = 0;
925 delete d->rootItem; d->rootItem = 0;
931 This function tries to release redundant resources currently held by the QML scene.
933 Calling this function might result in the scene graph and the OpenGL context used
934 for rendering being released to release graphics memory. If this happens, the
935 sceneGraphInvalidated() signal will be called, allowing users to clean up their
936 own graphics resources. The setPersistentOpenGLContext() and setPersistentSceneGraph()
937 functions can be used to prevent this from happening, if handling the cleanup is
938 not feasible in the application, at the cost of higher memory usage.
940 \sa sceneGraphInvalidated(), setPersistentOpenGLContext(), setPersistentSceneGraph().
943 void QQuickCanvas::releaseResources()
946 d->windowManager->releaseResources();
947 QQuickPixmap::purgeCache();
953 Controls whether the OpenGL context can be released as a part of a call to
956 The OpenGL context might still be released when the user makes an explicit
959 \sa setPersistentSceneGraph()
962 void QQuickCanvas::setPersistentOpenGLContext(bool persistent)
965 d->persistentGLContext = persistent;
970 Returns whether the OpenGL context can be released as a part of a call to
974 bool QQuickCanvas::isPersistentOpenGLContext() const
976 Q_D(const QQuickCanvas);
977 return d->persistentGLContext;
983 Controls whether the scene graph nodes and resources can be released as a
984 part of a call to releaseResources().
986 The scene graph nodes and resources might still be released when the user
987 makes an explicit call to hide().
989 \sa setPersistentOpenGLContext()
992 void QQuickCanvas::setPersistentSceneGraph(bool persistent)
995 d->persistentSceneGraph = persistent;
1001 Returns whether the scene graph nodes and resources can be released as a part
1002 of a call to releaseResources().
1005 bool QQuickCanvas::isPersistentSceneGraph() const
1007 Q_D(const QQuickCanvas);
1008 return d->persistentSceneGraph;
1016 Returns the invisible root item of the scene.
1018 A QQuickCanvas always has a single invisible root item. To add items to this canvas,
1019 reparent the items to the root item or to an existing item in the scene.
1021 QQuickItem *QQuickCanvas::rootItem() const
1023 Q_D(const QQuickCanvas);
1029 Returns the item which currently has active focus.
1031 QQuickItem *QQuickCanvas::activeFocusItem() const
1033 Q_D(const QQuickCanvas);
1035 return d->activeFocusItem;
1038 QObject *QQuickCanvas::focusObject() const
1040 Q_D(const QQuickCanvas);
1042 if (d->activeFocusItem)
1043 return d->activeFocusItem;
1044 return const_cast<QQuickCanvas*>(this);
1049 Returns the item which currently has the mouse grab.
1051 QQuickItem *QQuickCanvas::mouseGrabberItem() const
1053 Q_D(const QQuickCanvas);
1055 return d->mouseGrabberItem;
1060 \qmlproperty color QtQuick.Window2::Window::color
1062 The background color for the window.
1064 Setting this property is more efficient than using a separate Rectangle.
1067 bool QQuickCanvasPrivate::clearHover()
1070 if (hoverItems.isEmpty())
1073 QPointF pos = q->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint());
1075 bool accepted = false;
1076 foreach (QQuickItem* item, hoverItems)
1077 accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), true) || accepted;
1083 bool QQuickCanvas::event(QEvent *e)
1087 switch (e->type()) {
1089 case QEvent::TouchBegin:
1090 case QEvent::TouchUpdate:
1091 case QEvent::TouchEnd: {
1092 QTouchEvent *touch = static_cast<QTouchEvent*>(e);
1093 d->translateTouchEvent(touch);
1094 // return in order to avoid the QWindow::event below
1095 return d->deliverTouchEvent(touch);
1098 case QEvent::TouchCancel:
1099 // return in order to avoid the QWindow::event below
1100 return d->deliverTouchCancelEvent(static_cast<QTouchEvent*>(e));
1104 d->lastMousePosition = QPoint();
1106 #ifndef QT_NO_DRAGANDDROP
1107 case QEvent::DragEnter:
1108 case QEvent::DragLeave:
1109 case QEvent::DragMove:
1111 d->deliverDragEvent(&d->dragGrabber, e);
1114 case QEvent::WindowDeactivate:
1115 rootItem()->windowDeactivateEvent();
1117 case QEvent::FocusAboutToChange:
1118 if (d->activeFocusItem)
1119 qGuiApp->inputMethod()->commit();
1125 return QWindow::event(e);
1129 void QQuickCanvas::keyPressEvent(QKeyEvent *e)
1133 if (d->activeFocusItem)
1134 sendEvent(d->activeFocusItem, e);
1138 void QQuickCanvas::keyReleaseEvent(QKeyEvent *e)
1142 if (d->activeFocusItem)
1143 sendEvent(d->activeFocusItem, e);
1146 QMouseEvent *QQuickCanvasPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos)
1148 int caps = QGuiApplicationPrivate::mouseEventCaps(event);
1149 QVector2D velocity = QGuiApplicationPrivate::mouseEventVelocity(event);
1150 QMouseEvent *me = new QMouseEvent(event->type(),
1151 transformedLocalPos ? *transformedLocalPos : event->localPos(),
1152 event->windowPos(), event->screenPos(),
1153 event->button(), event->buttons(), event->modifiers());
1154 QGuiApplicationPrivate::setMouseEventCapsAndVelocity(me, caps, velocity);
1155 me->setTimestamp(event->timestamp());
1159 bool QQuickCanvasPrivate::deliverInitialMousePressEvent(QQuickItem *item, QMouseEvent *event)
1163 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1165 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1166 QPointF p = item->mapFromScene(event->windowPos());
1167 if (!item->contains(p))
1171 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1172 for (int ii = children.count() - 1; ii >= 0; --ii) {
1173 QQuickItem *child = children.at(ii);
1174 if (!child->isVisible() || !child->isEnabled())
1176 if (deliverInitialMousePressEvent(child, event))
1180 if (itemPrivate->acceptedMouseButtons() & event->button()) {
1181 QPointF localPos = item->mapFromScene(event->windowPos());
1182 if (item->contains(localPos)) {
1183 QScopedPointer<QMouseEvent> me(cloneMouseEvent(event, &localPos));
1186 q->sendEvent(item, me.data());
1187 event->setAccepted(me->isAccepted());
1188 if (me->isAccepted())
1190 if (mouseGrabberItem && !event->buttons())
1191 mouseGrabberItem->ungrabMouse();
1198 bool QQuickCanvasPrivate::deliverMouseEvent(QMouseEvent *event)
1202 lastMousePosition = event->windowPos();
1204 if (!mouseGrabberItem &&
1205 event->type() == QEvent::MouseButtonPress &&
1206 (event->buttons() & event->button()) == event->buttons()) {
1207 if (deliverInitialMousePressEvent(rootItem, event))
1211 return event->isAccepted();
1214 if (mouseGrabberItem) {
1215 QPointF localPos = mouseGrabberItem->mapFromScene(event->windowPos());
1216 QScopedPointer<QMouseEvent> me(cloneMouseEvent(event, &localPos));
1218 q->sendEvent(mouseGrabberItem, me.data());
1219 event->setAccepted(me->isAccepted());
1220 if (me->isAccepted())
1228 void QQuickCanvas::mousePressEvent(QMouseEvent *event)
1232 qWarning() << "QQuickCanvas::mousePressEvent()" << event->localPos() << event->button() << event->buttons();
1235 d->deliverMouseEvent(event);
1239 void QQuickCanvas::mouseReleaseEvent(QMouseEvent *event)
1243 qWarning() << "QQuickCanvas::mouseReleaseEvent()" << event->localPos() << event->button() << event->buttons();
1246 if (!d->mouseGrabberItem) {
1247 QWindow::mouseReleaseEvent(event);
1251 d->deliverMouseEvent(event);
1252 if (d->mouseGrabberItem && !event->buttons())
1253 d->mouseGrabberItem->ungrabMouse();
1257 void QQuickCanvas::mouseDoubleClickEvent(QMouseEvent *event)
1261 qWarning() << "QQuickCanvas::mouseDoubleClickEvent()" << event->localPos() << event->button() << event->buttons();
1264 if (!d->mouseGrabberItem && (event->buttons() & event->button()) == event->buttons()) {
1265 if (d->deliverInitialMousePressEvent(d->rootItem, event))
1272 d->deliverMouseEvent(event);
1275 bool QQuickCanvasPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
1276 const QPointF &scenePos, const QPointF &lastScenePos,
1277 Qt::KeyboardModifiers modifiers, bool accepted)
1280 const QTransform transform = QQuickItemPrivate::get(item)->canvasToItemTransform();
1282 //create copy of event
1283 QHoverEvent hoverEvent(type, transform.map(scenePos), transform.map(lastScenePos), modifiers);
1284 hoverEvent.setAccepted(accepted);
1286 q->sendEvent(item, &hoverEvent);
1288 return hoverEvent.isAccepted();
1292 void QQuickCanvas::mouseMoveEvent(QMouseEvent *event)
1296 qWarning() << "QQuickCanvas::mouseMoveEvent()" << event->localPos() << event->button() << event->buttons();
1299 if (!d->mouseGrabberItem) {
1300 if (d->lastMousePosition.isNull())
1301 d->lastMousePosition = event->windowPos();
1302 QPointF last = d->lastMousePosition;
1303 d->lastMousePosition = event->windowPos();
1305 bool accepted = event->isAccepted();
1306 bool delivered = d->deliverHoverEvent(d->rootItem, event->windowPos(), last, event->modifiers(), accepted);
1308 //take care of any exits
1309 accepted = d->clearHover();
1311 event->setAccepted(accepted);
1315 d->deliverMouseEvent(event);
1318 bool QQuickCanvasPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
1319 Qt::KeyboardModifiers modifiers, bool &accepted)
1321 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1323 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1324 QPointF p = item->mapFromScene(scenePos);
1325 if (!item->contains(p))
1329 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1330 for (int ii = children.count() - 1; ii >= 0; --ii) {
1331 QQuickItem *child = children.at(ii);
1332 if (!child->isVisible() || !child->isEnabled())
1334 if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, accepted))
1338 if (itemPrivate->hoverEnabled) {
1339 QPointF p = item->mapFromScene(scenePos);
1340 if (item->contains(p)) {
1341 if (!hoverItems.isEmpty() && hoverItems[0] == item) {
1343 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1345 QList<QQuickItem *> itemsToHover;
1346 QQuickItem* parent = item;
1347 itemsToHover << item;
1348 while ((parent = parent->parentItem()))
1349 itemsToHover << parent;
1351 // Leaving from previous hovered items until we reach the item or one of its ancestors.
1352 while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems[0])) {
1353 sendHoverEvent(QEvent::HoverLeave, hoverItems[0], scenePos, lastScenePos, modifiers, accepted);
1354 hoverItems.removeFirst();
1357 if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item
1358 // ### Shouldn't we send moves for the parent items as well?
1359 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1361 // Enter items that are not entered yet.
1363 if (!hoverItems.isEmpty())
1364 startIdx = itemsToHover.indexOf(hoverItems[0]) - 1;
1366 startIdx = itemsToHover.count() - 1;
1368 for (int i = startIdx; i >= 0; i--) {
1369 QQuickItem *itemToHover = itemsToHover[i];
1370 if (QQuickItemPrivate::get(itemToHover)->hoverEnabled) {
1371 hoverItems.prepend(itemToHover);
1372 sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, accepted);
1384 bool QQuickCanvasPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event)
1387 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1389 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1390 QPointF p = item->mapFromScene(event->posF());
1391 if (!item->contains(p))
1395 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1396 for (int ii = children.count() - 1; ii >= 0; --ii) {
1397 QQuickItem *child = children.at(ii);
1398 if (!child->isVisible() || !child->isEnabled())
1400 if (deliverWheelEvent(child, event))
1404 QPointF p = item->mapFromScene(event->posF());
1406 if (item->contains(p)) {
1407 QWheelEvent wheel(p, p, event->pixelDelta(), event->angleDelta(), event->delta(),
1408 event->orientation(), event->buttons(), event->modifiers());
1410 q->sendEvent(item, &wheel);
1411 if (wheel.isAccepted()) {
1420 #ifndef QT_NO_WHEELEVENT
1422 void QQuickCanvas::wheelEvent(QWheelEvent *event)
1426 qWarning() << "QQuickCanvas::wheelEvent()" << event->pixelDelta() << event->angleDelta();
1429 //if the actual wheel event was accepted, accept the compatability wheel event and return early
1430 if (d->lastWheelEventAccepted && event->angleDelta().isNull())
1434 d->deliverWheelEvent(d->rootItem, event);
1435 d->lastWheelEventAccepted = event->isAccepted();
1437 #endif // QT_NO_WHEELEVENT
1440 bool QQuickCanvasPrivate::deliverTouchCancelEvent(QTouchEvent *event)
1443 qWarning("touchCancelEvent");
1446 // A TouchCancel event will typically not contain any points.
1447 // Deliver it to all items that have active touches.
1448 QSet<QQuickItem *> cancelDelivered;
1449 foreach (QQuickItem *item, itemForTouchPointId) {
1450 if (cancelDelivered.contains(item))
1452 cancelDelivered.insert(item);
1453 q->sendEvent(item, event);
1456 if (mouseGrabberItem)
1457 mouseGrabberItem->ungrabMouse();
1458 // The next touch event can only be a TouchBegin so clean up.
1459 itemForTouchPointId.clear();
1463 // check what kind of touch we have (begin/update) and
1464 // call deliverTouchPoints to actually dispatch the points
1465 bool QQuickCanvasPrivate::deliverTouchEvent(QTouchEvent *event)
1468 if (event->type() == QEvent::TouchBegin)
1469 qWarning() << "touchBeginEvent";
1470 else if (event->type() == QEvent::TouchUpdate)
1471 qWarning() << "touchUpdateEvent points";
1472 else if (event->type() == QEvent::TouchEnd)
1473 qWarning("touchEndEvent");
1476 // List of all items that received an event before
1477 // When we have TouchBegin this is and will stay empty
1478 QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > updatedPoints;
1480 // Figure out who accepted a touch point last and put it in updatedPoints
1481 // Add additional item to newPoints
1482 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
1483 QList<QTouchEvent::TouchPoint> newPoints;
1484 for (int i=0; i<touchPoints.count(); i++) {
1485 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
1486 if (touchPoint.state() == Qt::TouchPointPressed) {
1487 newPoints << touchPoint;
1489 // TouchPointStationary is relevant only to items which
1490 // are also receiving touch points with some other state.
1491 // But we have not yet decided which points go to which item,
1492 // so for now we must include all non-new points in updatedPoints.
1493 if (itemForTouchPointId.contains(touchPoint.id())) {
1494 QQuickItem *item = itemForTouchPointId.value(touchPoint.id());
1496 updatedPoints[item].append(touchPoint);
1501 // Deliver the event, but only if there is at least one new point
1502 // or some item accepted a point and should receive an update
1503 if (newPoints.count() > 0 || updatedPoints.count() > 0) {
1504 QSet<int> acceptedNewPoints;
1505 event->setAccepted(deliverTouchPoints(rootItem, event, newPoints, &acceptedNewPoints, &updatedPoints));
1509 // Remove released points from itemForTouchPointId
1510 if (event->touchPointStates() & Qt::TouchPointReleased) {
1511 for (int i=0; i<touchPoints.count(); i++) {
1512 if (touchPoints[i].state() == Qt::TouchPointReleased) {
1513 itemForTouchPointId.remove(touchPoints[i].id());
1514 if (touchPoints[i].id() == touchMouseId)
1520 if (event->type() == QEvent::TouchEnd) {
1521 Q_ASSERT(itemForTouchPointId.isEmpty());
1524 return event->isAccepted();
1527 // This function recurses and sends the events to the individual items
1528 bool QQuickCanvasPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints, QSet<int> *acceptedNewPoints, QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *updatedPoints)
1530 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1532 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1533 for (int i=0; i<newPoints.count(); i++) {
1534 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1535 if (!item->contains(p))
1540 // Check if our children want the event (or parts of it)
1541 // This is the only point where touch event delivery recurses!
1542 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1543 for (int ii = children.count() - 1; ii >= 0; --ii) {
1544 QQuickItem *child = children.at(ii);
1545 if (!child->isEnabled() || !child->isVisible())
1547 if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints))
1551 // None of the children accepted the event, so check the given item itself.
1552 // First, construct matchingPoints as a list of TouchPoints which the
1553 // given item might be interested in. Any newly-pressed point which is
1554 // inside the item's bounds will be interesting, and also any updated point
1555 // which was already accepted by that item when it was first pressed.
1556 // (A point which was already accepted is effectively "grabbed" by the item.)
1558 // set of IDs of "interesting" new points
1559 QSet<int> matchingNewPoints;
1560 // set of points which this item has previously accepted, for starters
1561 QList<QTouchEvent::TouchPoint> matchingPoints = (*updatedPoints)[item];
1562 // now add the new points which are inside this item's bounds
1563 if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) {
1564 for (int i = 0; i < newPoints.count(); i++) {
1565 if (acceptedNewPoints->contains(newPoints[i].id()))
1567 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1568 if (item->contains(p)) {
1569 matchingNewPoints.insert(newPoints[i].id());
1570 matchingPoints << newPoints[i];
1574 // If there are no matching new points, and the existing points are all stationary,
1575 // there's no need to send an event to this item. This is required by a test in
1576 // tst_qquickcanvas::touchEvent_basic:
1577 // a single stationary press on an item shouldn't cause an event
1578 if (matchingNewPoints.isEmpty()) {
1579 bool stationaryOnly = true;
1580 Q_FOREACH (QTouchEvent::TouchPoint tp, matchingPoints)
1581 if (tp.state() != Qt::TouchPointStationary)
1582 stationaryOnly = false;
1584 matchingPoints.clear();
1587 if (!matchingPoints.isEmpty()) {
1588 // Now we know this item might be interested in the event. Copy and send it, but
1589 // with only the subset of TouchPoints which are relevant to that item: that's matchingPoints.
1590 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1591 transformTouchPoints(matchingPoints, itemPrivate->canvasToItemTransform());
1592 deliverMatchingPointsToItem(item, event, acceptedNewPoints, matchingNewPoints, matchingPoints);
1595 // record the fact that this item has been visited already
1596 updatedPoints->remove(item);
1598 // recursion is done only if ALL touch points have been delivered
1599 return (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty());
1602 // touchEventForItemBounds has no means to generate a touch event that contains
1603 // only the points that are relevant for this item. Thus the need for
1604 // matchingPoints to already be that set of interesting points.
1605 // They are all pre-transformed, too.
1606 bool QQuickCanvasPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints)
1608 QScopedPointer<QTouchEvent> touchEvent(touchEventWithPoints(*event, matchingPoints));
1609 touchEvent.data()->setTarget(item);
1610 bool touchEventAccepted = false;
1612 // First check whether the parent wants to be a filter,
1613 // and if the parent accepts the event we are done.
1614 if (sendFilteredTouchEvent(item->parentItem(), item, event)) {
1619 // Since it can change in sendEvent, update itemForTouchPointId now
1620 foreach (int id, matchingNewPoints)
1621 itemForTouchPointId[id] = item;
1623 // Deliver the touch event to the given item
1624 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1625 itemPrivate->deliverTouchEvent(touchEvent.data());
1626 touchEventAccepted = touchEvent->isAccepted();
1628 // If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it.
1629 if (!touchEventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) {
1631 event->setAccepted(translateTouchToMouse(item, event));
1632 if (event->isAccepted()) {
1633 touchEventAccepted = true;
1637 if (touchEventAccepted) {
1638 // If the touch was accepted (regardless by whom or in what form),
1639 // update acceptedNewPoints.
1640 foreach (int id, matchingNewPoints)
1641 acceptedNewPoints->insert(id);
1643 // But if the event was not accepted then we know this item
1644 // will not be interested in further updates for those touchpoint IDs either.
1645 foreach (int id, matchingNewPoints)
1646 if (itemForTouchPointId[id] == item)
1647 itemForTouchPointId.remove(id);
1650 return touchEventAccepted;
1653 QTouchEvent *QQuickCanvasPrivate::touchEventForItemBounds(QQuickItem *target, const QTouchEvent &originalEvent)
1655 const QList<QTouchEvent::TouchPoint> &touchPoints = originalEvent.touchPoints();
1656 QList<QTouchEvent::TouchPoint> pointsInBounds;
1657 // if all points are stationary, the list of points should be empty to signal a no-op
1658 if (originalEvent.touchPointStates() != Qt::TouchPointStationary) {
1659 for (int i = 0; i < touchPoints.count(); ++i) {
1660 const QTouchEvent::TouchPoint &tp = touchPoints.at(i);
1661 if (tp.state() == Qt::TouchPointPressed) {
1662 QPointF p = target->mapFromScene(tp.scenePos());
1663 if (target->contains(p))
1664 pointsInBounds.append(tp);
1666 pointsInBounds.append(tp);
1669 transformTouchPoints(pointsInBounds, QQuickItemPrivate::get(target)->canvasToItemTransform());
1672 QTouchEvent* touchEvent = touchEventWithPoints(originalEvent, pointsInBounds);
1673 touchEvent->setTarget(target);
1677 QTouchEvent *QQuickCanvasPrivate::touchEventWithPoints(const QTouchEvent &event, const QList<QTouchEvent::TouchPoint> &newPoints)
1679 Qt::TouchPointStates eventStates;
1680 for (int i=0; i<newPoints.count(); i++)
1681 eventStates |= newPoints[i].state();
1682 // if all points have the same state, set the event type accordingly
1683 QEvent::Type eventType = event.type();
1684 switch (eventStates) {
1685 case Qt::TouchPointPressed:
1686 eventType = QEvent::TouchBegin;
1688 case Qt::TouchPointReleased:
1689 eventType = QEvent::TouchEnd;
1692 eventType = QEvent::TouchUpdate;
1696 QTouchEvent *touchEvent = new QTouchEvent(eventType);
1697 touchEvent->setWindow(event.window());
1698 touchEvent->setTarget(event.target());
1699 touchEvent->setDevice(event.device());
1700 touchEvent->setModifiers(event.modifiers());
1701 touchEvent->setTouchPoints(newPoints);
1702 touchEvent->setTouchPointStates(eventStates);
1703 touchEvent->setTimestamp(event.timestamp());
1704 touchEvent->accept();
1708 #ifndef QT_NO_DRAGANDDROP
1709 void QQuickCanvasPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event)
1712 grabber->resetTarget();
1713 QQuickDragGrabber::iterator grabItem = grabber->begin();
1714 if (grabItem != grabber->end()) {
1715 Q_ASSERT(event->type() != QEvent::DragEnter);
1716 if (event->type() == QEvent::Drop) {
1717 QDropEvent *e = static_cast<QDropEvent *>(event);
1718 for (e->setAccepted(false); !e->isAccepted() && grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1719 QPointF p = (**grabItem)->mapFromScene(e->pos());
1720 QDropEvent translatedEvent(
1722 e->possibleActions(),
1725 e->keyboardModifiers());
1726 QQuickDropEventEx::copyActions(&translatedEvent, *e);
1727 q->sendEvent(**grabItem, &translatedEvent);
1728 e->setAccepted(translatedEvent.isAccepted());
1729 e->setDropAction(translatedEvent.dropAction());
1730 grabber->setTarget(**grabItem);
1733 if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave.
1734 QDragLeaveEvent leaveEvent;
1735 for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
1736 q->sendEvent(**grabItem, &leaveEvent);
1738 } else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1739 QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event);
1740 if (deliverDragEvent(grabber, **grabItem, moveEvent)) {
1741 moveEvent->setAccepted(true);
1742 for (++grabItem; grabItem != grabber->end();) {
1743 QPointF p = (**grabItem)->mapFromScene(moveEvent->pos());
1744 if ((**grabItem)->contains(p)) {
1745 QDragMoveEvent translatedEvent(
1747 moveEvent->possibleActions(),
1748 moveEvent->mimeData(),
1749 moveEvent->mouseButtons(),
1750 moveEvent->keyboardModifiers());
1751 QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent);
1752 q->sendEvent(**grabItem, &translatedEvent);
1755 QDragLeaveEvent leaveEvent;
1756 q->sendEvent(**grabItem, &leaveEvent);
1757 grabItem = grabber->release(grabItem);
1762 QDragLeaveEvent leaveEvent;
1763 q->sendEvent(**grabItem, &leaveEvent);
1767 if (event->type() == QEvent::DragEnter || event->type() == QEvent::DragMove) {
1768 QDragMoveEvent *e = static_cast<QDragMoveEvent *>(event);
1769 QDragEnterEvent enterEvent(
1771 e->possibleActions(),
1774 e->keyboardModifiers());
1775 QQuickDropEventEx::copyActions(&enterEvent, *e);
1776 event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent));
1780 bool QQuickCanvasPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event)
1783 bool accepted = false;
1784 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1785 if (!item->isVisible() || !item->isEnabled())
1788 QPointF p = item->mapFromScene(event->pos());
1789 if (item->contains(p)) {
1790 if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) {
1791 QDragMoveEvent translatedEvent(
1793 event->possibleActions(),
1795 event->mouseButtons(),
1796 event->keyboardModifiers(),
1798 QQuickDropEventEx::copyActions(&translatedEvent, *event);
1799 q->sendEvent(item, &translatedEvent);
1800 if (event->type() == QEvent::DragEnter) {
1801 if (translatedEvent.isAccepted()) {
1802 grabber->grab(item);
1809 } else if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1813 QDragEnterEvent enterEvent(
1815 event->possibleActions(),
1817 event->mouseButtons(),
1818 event->keyboardModifiers());
1819 QQuickDropEventEx::copyActions(&enterEvent, *event);
1820 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1821 for (int ii = children.count() - 1; ii >= 0; --ii) {
1822 if (deliverDragEvent(grabber, children.at(ii), &enterEvent))
1828 #endif // QT_NO_DRAGANDDROP
1830 bool QQuickCanvasPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event)
1835 QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target);
1836 if (targetPrivate->filtersChildMouseEvents) {
1837 QScopedPointer<QTouchEvent> targetEvent(touchEventForItemBounds(target, *event));
1838 if (!targetEvent->touchPoints().isEmpty()) {
1839 QVector<int> touchIds;
1840 for (int i = 0; i < event->touchPoints().size(); ++i)
1841 touchIds.append(event->touchPoints().at(i).id());
1842 if (target->childMouseEventFilter(item, targetEvent.data())) {
1843 target->grabTouchPoints(touchIds);
1844 if (mouseGrabberItem) {
1845 mouseGrabberItem->ungrabMouse();
1851 // Only offer a mouse event to the filter if we have one point
1852 if (targetEvent->touchPoints().count() == 1) {
1854 const QTouchEvent::TouchPoint &tp = targetEvent->touchPoints().first();
1855 switch (tp.state()) {
1856 case Qt::TouchPointPressed:
1857 t = QEvent::MouseButtonPress;
1859 case Qt::TouchPointReleased:
1860 t = QEvent::MouseButtonRelease;
1863 // move or stationary
1864 t = QEvent::MouseMove;
1868 // targetEvent is already transformed wrt local position, velocity, etc.
1869 QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, targetEvent->touchPoints().first(), event, item, false));
1870 if (target->childMouseEventFilter(item, mouseEvent.data())) {
1871 itemForTouchPointId[tp.id()] = target;
1872 touchMouseId = tp.id();
1873 target->grabMouse();
1880 return sendFilteredTouchEvent(target->parentItem(), item, event);
1883 bool QQuickCanvasPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QEvent *event)
1888 QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target);
1889 if (targetPrivate->filtersChildMouseEvents)
1890 if (target->childMouseEventFilter(item, event))
1893 if (sendFilteredMouseEvent(target->parentItem(), item, event))
1899 bool QQuickCanvasPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event)
1901 QStyleHints *styleHints = qApp->styleHints();
1902 int caps = QGuiApplicationPrivate::mouseEventCaps(event);
1903 bool dragVelocityLimitAvailable = (caps & QTouchDevice::Velocity)
1904 && styleHints->startDragVelocity();
1905 bool overThreshold = qAbs(d) > styleHints->startDragDistance();
1906 if (dragVelocityLimitAvailable) {
1907 QVector2D velocityVec = QGuiApplicationPrivate::mouseEventVelocity(event);
1908 qreal velocity = axis == Qt::XAxis ? velocityVec.x() : velocityVec.y();
1909 overThreshold |= qAbs(velocity) > styleHints->startDragVelocity();
1911 return overThreshold;
1915 Propagates an event to a QQuickItem on the canvas
1917 bool QQuickCanvas::sendEvent(QQuickItem *item, QEvent *e)
1922 qWarning("QQuickCanvas::sendEvent: Cannot send event to a null item");
1928 switch (e->type()) {
1929 case QEvent::KeyPress:
1930 case QEvent::KeyRelease:
1932 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1933 while (!e->isAccepted() && (item = item->parentItem())) {
1935 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1938 case QEvent::FocusIn:
1939 case QEvent::FocusOut:
1940 QQuickItemPrivate::get(item)->deliverFocusEvent(static_cast<QFocusEvent *>(e));
1942 case QEvent::MouseButtonPress:
1943 case QEvent::MouseButtonRelease:
1944 case QEvent::MouseButtonDblClick:
1945 case QEvent::MouseMove:
1946 // XXX todo - should sendEvent be doing this? how does it relate to forwarded events?
1947 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
1948 // accept because qml items by default accept and have to explicitly opt out of accepting
1950 QQuickItemPrivate::get(item)->deliverMouseEvent(static_cast<QMouseEvent *>(e));
1953 case QEvent::UngrabMouse:
1954 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
1956 item->mouseUngrabEvent();
1960 QQuickItemPrivate::get(item)->deliverWheelEvent(static_cast<QWheelEvent *>(e));
1962 case QEvent::HoverEnter:
1963 case QEvent::HoverLeave:
1964 case QEvent::HoverMove:
1965 QQuickItemPrivate::get(item)->deliverHoverEvent(static_cast<QHoverEvent *>(e));
1967 case QEvent::TouchBegin:
1968 case QEvent::TouchUpdate:
1969 case QEvent::TouchEnd:
1970 d->sendFilteredTouchEvent(item->parentItem(), item, static_cast<QTouchEvent *>(e));
1972 case QEvent::TouchCancel:
1973 QQuickItemPrivate::get(item)->deliverTouchEvent(static_cast<QTouchEvent *>(e));
1975 #ifndef QT_NO_DRAGANDDROP
1976 case QEvent::DragEnter:
1977 case QEvent::DragMove:
1978 case QEvent::DragLeave:
1980 QQuickItemPrivate::get(item)->deliverDragEvent(e);
1990 void QQuickCanvasPrivate::cleanupNodes()
1992 for (int ii = 0; ii < cleanupNodeList.count(); ++ii)
1993 delete cleanupNodeList.at(ii);
1994 cleanupNodeList.clear();
1997 void QQuickCanvasPrivate::cleanupNodesOnShutdown(QQuickItem *item)
1999 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
2000 if (p->itemNodeInstance) {
2001 delete p->itemNodeInstance;
2002 p->itemNodeInstance = 0;
2004 if (p->extra.isAllocated()) {
2005 p->extra->opacityNode = 0;
2006 p->extra->clipNode = 0;
2007 p->extra->rootNode = 0;
2014 for (int ii = 0; ii < p->childItems.count(); ++ii)
2015 cleanupNodesOnShutdown(p->childItems.at(ii));
2018 // This must be called from the render thread, with the main thread frozen
2019 void QQuickCanvasPrivate::cleanupNodesOnShutdown()
2023 cleanupNodesOnShutdown(rootItem);
2024 QSet<QQuickItem *>::const_iterator it = parentlessItems.begin();
2025 for (; it != parentlessItems.end(); ++it)
2026 cleanupNodesOnShutdown(*it);
2027 q->cleanupSceneGraph();
2030 void QQuickCanvasPrivate::updateDirtyNodes()
2033 qWarning() << "QQuickCanvasPrivate::updateDirtyNodes():";
2038 QQuickItem *updateList = dirtyItemList;
2040 if (updateList) QQuickItemPrivate::get(updateList)->prevDirtyItem = &updateList;
2042 while (updateList) {
2043 QQuickItem *item = updateList;
2044 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
2045 itemPriv->removeFromDirtyList();
2048 qWarning() << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString());
2050 updateDirtyNode(item);
2054 void QQuickCanvasPrivate::updateDirtyNode(QQuickItem *item)
2056 #ifdef QML_RUNTIME_TESTING
2057 bool didFlash = false;
2060 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
2061 quint32 dirty = itemPriv->dirtyAttributes;
2062 itemPriv->dirtyAttributes = 0;
2064 if ((dirty & QQuickItemPrivate::TransformUpdateMask) ||
2065 (dirty & QQuickItemPrivate::Size && itemPriv->origin() != QQuickItem::TopLeft &&
2066 (itemPriv->scale() != 1. || itemPriv->rotation() != 0.))) {
2070 if (itemPriv->x != 0. || itemPriv->y != 0.)
2071 matrix.translate(itemPriv->x, itemPriv->y);
2073 for (int ii = itemPriv->transforms.count() - 1; ii >= 0; --ii)
2074 itemPriv->transforms.at(ii)->applyTo(&matrix);
2076 if (itemPriv->scale() != 1. || itemPriv->rotation() != 0.) {
2077 QPointF origin = item->transformOriginPoint();
2078 matrix.translate(origin.x(), origin.y());
2079 if (itemPriv->scale() != 1.)
2080 matrix.scale(itemPriv->scale(), itemPriv->scale());
2081 if (itemPriv->rotation() != 0.)
2082 matrix.rotate(itemPriv->rotation(), 0, 0, 1);
2083 matrix.translate(-origin.x(), -origin.y());
2086 itemPriv->itemNode()->setMatrix(matrix);
2089 bool clipEffectivelyChanged = (dirty & (QQuickItemPrivate::Clip | QQuickItemPrivate::Canvas)) &&
2090 ((item->clip() == false) != (itemPriv->clipNode() == 0));
2091 int effectRefCount = itemPriv->extra.isAllocated()?itemPriv->extra->effectRefCount:0;
2092 bool effectRefEffectivelyChanged = (dirty & (QQuickItemPrivate::EffectReference | QQuickItemPrivate::Canvas)) &&
2093 ((effectRefCount == 0) != (itemPriv->rootNode() == 0));
2095 if (clipEffectivelyChanged) {
2096 QSGNode *parent = itemPriv->opacityNode() ? (QSGNode *) itemPriv->opacityNode() :
2097 (QSGNode *)itemPriv->itemNode();
2098 QSGNode *child = itemPriv->rootNode() ? (QSGNode *)itemPriv->rootNode() :
2099 (QSGNode *)itemPriv->groupNode;
2102 Q_ASSERT(itemPriv->clipNode() == 0);
2103 itemPriv->extra.value().clipNode = new QQuickDefaultClipNode(item->clipRect());
2104 itemPriv->clipNode()->update();
2107 parent->removeChildNode(child);
2108 parent->appendChildNode(itemPriv->clipNode());
2110 itemPriv->clipNode()->appendChildNode(child);
2113 Q_ASSERT(itemPriv->clipNode() != 0);
2114 parent->removeChildNode(itemPriv->clipNode());
2116 itemPriv->clipNode()->removeChildNode(child);
2117 delete itemPriv->clipNode();
2118 itemPriv->extra->clipNode = 0;
2120 parent->appendChildNode(child);
2124 if (dirty & QQuickItemPrivate::ChildrenUpdateMask)
2125 itemPriv->childContainerNode()->removeAllChildNodes();
2127 if (effectRefEffectivelyChanged) {
2128 QSGNode *parent = itemPriv->clipNode();
2130 parent = itemPriv->opacityNode();
2132 parent = itemPriv->itemNode();
2133 QSGNode *child = itemPriv->groupNode;
2135 if (itemPriv->extra.isAllocated() && itemPriv->extra->effectRefCount) {
2136 Q_ASSERT(itemPriv->rootNode() == 0);
2137 itemPriv->extra->rootNode = new QSGRootNode;
2140 parent->removeChildNode(child);
2141 parent->appendChildNode(itemPriv->rootNode());
2143 itemPriv->rootNode()->appendChildNode(child);
2145 Q_ASSERT(itemPriv->rootNode() != 0);
2146 parent->removeChildNode(itemPriv->rootNode());
2148 itemPriv->rootNode()->removeChildNode(child);
2149 delete itemPriv->rootNode();
2150 itemPriv->extra->rootNode = 0;
2152 parent->appendChildNode(child);
2156 if (dirty & QQuickItemPrivate::ChildrenUpdateMask) {
2157 QSGNode *groupNode = itemPriv->groupNode;
2159 groupNode->removeAllChildNodes();
2161 QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems();
2164 for (; ii < orderedChildren.count() && orderedChildren.at(ii)->z() < 0; ++ii) {
2165 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
2166 if (!childPrivate->explicitVisible &&
2167 (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount))
2169 if (childPrivate->itemNode()->parent())
2170 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
2172 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
2175 QSGNode *beforePaintNode = itemPriv->groupNode ? itemPriv->groupNode->lastChild() : 0;
2176 if (beforePaintNode || itemPriv->extra.isAllocated())
2177 itemPriv->extra.value().beforePaintNode = beforePaintNode;
2179 if (itemPriv->paintNode)
2180 itemPriv->childContainerNode()->appendChildNode(itemPriv->paintNode);
2182 for (; ii < orderedChildren.count(); ++ii) {
2183 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
2184 if (!childPrivate->explicitVisible &&
2185 (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount))
2187 if (childPrivate->itemNode()->parent())
2188 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
2190 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
2194 if ((dirty & QQuickItemPrivate::Size) && itemPriv->clipNode()) {
2195 itemPriv->clipNode()->setRect(item->clipRect());
2196 itemPriv->clipNode()->update();
2199 if (dirty & (QQuickItemPrivate::OpacityValue | QQuickItemPrivate::Visible
2200 | QQuickItemPrivate::HideReference | QQuickItemPrivate::Canvas))
2202 qreal opacity = itemPriv->explicitVisible && (!itemPriv->extra.isAllocated() || itemPriv->extra->hideRefCount == 0)
2203 ? itemPriv->opacity() : qreal(0);
2205 if (opacity != 1 && !itemPriv->opacityNode()) {
2206 itemPriv->extra.value().opacityNode = new QSGOpacityNode;
2208 QSGNode *parent = itemPriv->itemNode();
2209 QSGNode *child = itemPriv->clipNode();
2211 child = itemPriv->rootNode();
2213 child = itemPriv->groupNode;
2216 parent->removeChildNode(child);
2217 parent->appendChildNode(itemPriv->opacityNode());
2219 itemPriv->opacityNode()->appendChildNode(child);
2221 if (itemPriv->opacityNode())
2222 itemPriv->opacityNode()->setOpacity(opacity);
2225 if (dirty & QQuickItemPrivate::ContentUpdateMask) {
2227 if (itemPriv->flags & QQuickItem::ItemHasContents) {
2228 updatePaintNodeData.transformNode = itemPriv->itemNode();
2229 itemPriv->paintNode = item->updatePaintNode(itemPriv->paintNode, &updatePaintNodeData);
2231 Q_ASSERT(itemPriv->paintNode == 0 ||
2232 itemPriv->paintNode->parent() == 0 ||
2233 itemPriv->paintNode->parent() == itemPriv->childContainerNode());
2235 if (itemPriv->paintNode && itemPriv->paintNode->parent() == 0) {
2236 if (itemPriv->extra.isAllocated() && itemPriv->extra->beforePaintNode)
2237 itemPriv->childContainerNode()->insertChildNodeAfter(itemPriv->paintNode, itemPriv->extra->beforePaintNode);
2239 itemPriv->childContainerNode()->prependChildNode(itemPriv->paintNode);
2241 } else if (itemPriv->paintNode) {
2242 delete itemPriv->paintNode;
2243 itemPriv->paintNode = 0;
2248 // Check consistency.
2249 const QSGNode *nodeChain[] = {
2250 itemPriv->itemNodeInstance,
2251 itemPriv->opacityNode(),
2252 itemPriv->clipNode(),
2253 itemPriv->rootNode(),
2254 itemPriv->groupNode,
2255 itemPriv->paintNode,
2260 while (ip < 5 && nodeChain[ip] == 0)
2265 while (ic < 5 && nodeChain[ic] == 0)
2267 const QSGNode *parent = nodeChain[ip];
2268 const QSGNode *child = nodeChain[ic];
2270 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 0);
2272 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 1);
2273 Q_ASSERT(child->parent() == parent);
2274 bool containsChild = false;
2275 for (QSGNode *n = parent->firstChild(); n; n = n->nextSibling())
2276 containsChild |= (n == child);
2277 Q_ASSERT(containsChild);
2283 #ifdef QML_RUNTIME_TESTING
2284 if (itemPriv->sceneGraphContext()->isFlashModeEnabled()) {
2285 QSGFlashNode *flash = new QSGFlashNode();
2286 flash->setRect(item->boundingRect());
2287 itemPriv->childContainerNode()->appendChildNode(flash);
2298 void QQuickCanvas::maybeUpdate()
2301 d->windowManager->maybeUpdate(this);
2304 void QQuickCanvas::cleanupSceneGraph()
2311 delete d->renderer->rootNode();
2318 Returns the opengl context used for rendering.
2320 If the scene graph is not ready, this function will return 0.
2322 \sa sceneGraphInitialized(), sceneGraphInvalidated()
2325 QOpenGLContext *QQuickCanvas::openglContext() const
2327 Q_D(const QQuickCanvas);
2328 if (d->context->isReady())
2329 return d->context->glContext();
2335 \fn void QSGContext::sceneGraphInitialized()
2337 This signal is emitted when the scene graph has been initialized.
2339 This signal will be emitted from the scene graph rendering thread.
2345 \fn void QSGContext::sceneGraphInvalidated()
2347 This signal is emitted when the scene graph has been invalidated.
2349 This signal implies that the opengl rendering context used
2350 has been invalidated and all user resources tied to that context
2353 This signal will be emitted from the scene graph rendering thread.
2358 Sets the render target for this canvas to be \a fbo.
2360 The specified fbo must be created in the context of the canvas
2361 or one that shares with it.
2364 This function can only be called from the thread doing
2368 void QQuickCanvas::setRenderTarget(QOpenGLFramebufferObject *fbo)
2371 if (d->context && d->context && QThread::currentThread() != d->context->thread()) {
2372 qWarning("QQuickCanvas::setRenderThread: Cannot set render target from outside the rendering thread");
2376 d->renderTarget = fbo;
2378 d->renderTargetId = fbo->handle();
2379 d->renderTargetSize = fbo->size();
2381 d->renderTargetId = 0;
2382 d->renderTargetSize = QSize();
2389 void QQuickCanvas::setRenderTarget(uint fboId, const QSize &size)
2392 if (d->context && d->context && QThread::currentThread() != d->context->thread()) {
2393 qWarning("QQuickCanvas::setRenderThread: Cannot set render target from outside the rendering thread");
2397 d->renderTargetId = fboId;
2398 d->renderTargetSize = size;
2400 // Unset any previously set instance...
2401 d->renderTarget = 0;
2406 Returns the FBO id of the render target when set; otherwise returns 0.
2408 uint QQuickCanvas::renderTargetId() const
2410 Q_D(const QQuickCanvas);
2411 return d->renderTargetId;
2415 Returns the size of the currently set render target; otherwise returns an enpty size.
2417 QSize QQuickCanvas::renderTargetSize() const
2419 Q_D(const QQuickCanvas);
2420 return d->renderTargetSize;
2427 Returns the render target for this canvas.
2429 The default is to render to the surface of the canvas, in which
2430 case the render target is 0.
2432 QOpenGLFramebufferObject *QQuickCanvas::renderTarget() const
2434 Q_D(const QQuickCanvas);
2435 return d->renderTarget;
2440 Grabs the contents of the framebuffer and returns it as an image.
2442 This function might not work if the view is not visible.
2444 \warning Calling this function will cause performance problems.
2446 \warning This function can only be called from the GUI thread.
2448 QImage QQuickCanvas::grabFrameBuffer()
2451 return d->windowManager->grab(this);
2455 Returns an incubation controller that splices incubation between frames
2456 for this canvas. QQuickView automatically installs this controller for you,
2457 otherwise you will need to install it yourself using \l{QQmlEngine::setIncubationController}
2459 The controller is owned by the canvas and will be destroyed when the canvas
2462 QQmlIncubationController *QQuickCanvas::incubationController() const
2464 Q_D(const QQuickCanvas);
2466 if (!d->incubationController)
2467 d->incubationController = new QQuickCanvasIncubationController(const_cast<QQuickCanvasPrivate *>(d));
2468 return d->incubationController;
2474 \enum QQuickCanvas::CreateTextureOption
2476 The CreateTextureOption enums are used to customize a texture is wrapped.
2478 \value TextureHasAlphaChannel The texture has an alpha channel and should
2479 be drawn using blending.
2481 \value TextureHasMipmaps The texture has mipmaps and can be drawn with
2484 \value TextureOwnsGLTexture The texture object owns the texture id and
2485 will delete the GL texture when the texture object is deleted.
2489 \fn void QQuickCanvas::beforeRendering()
2491 This signal is emitted before the scene starts rendering.
2493 Combined with the modes for clearing the background, this option
2494 can be used to paint using raw GL under QML content.
2496 The GL context used for rendering the scene graph will be bound
2499 \warning Since this signal is emitted from the scene graph rendering thread, the
2500 receiver should be on the scene graph thread or the connection should be Qt::DirectConnection.
2502 \warning Make very sure that a signal handler for beforeRendering leaves the GL
2503 context in the same state as it was when the signal handler was entered. Failing to
2504 do so can result in the scene not rendering properly.
2508 \fn void QQuickCanvas::afterRendering()
2510 This signal is emitted after the scene has completed rendering, before swapbuffers is called.
2512 This signal can be used to paint using raw GL on top of QML content,
2513 or to do screen scraping of the current frame buffer.
2515 The GL context used for rendering the scene graph will be bound at this point.
2517 \warning Since this signal is emitted from the scene graph rendering thread, the
2518 receiver should be on the scene graph thread or the connection should be Qt::DirectConnection.
2520 \warning Make very sure that a signal handler for afterRendering() leaves the GL
2521 context in the same state as it was when the signal handler was entered. Failing to
2522 do so can result in the scene not rendering properly.
2528 Sets weither the scene graph rendering of QML should clear the color buffer
2529 before it starts rendering to \a enbled.
2531 By disabling clearing of the color buffer, it is possible to do GL painting
2532 under the scene graph.
2534 The color buffer is cleared by default.
2536 \sa beforeRendering()
2539 void QQuickCanvas::setClearBeforeRendering(bool enabled)
2542 d->clearBeforeRendering = enabled;
2548 Returns weither clearing of the color buffer is done before rendering or not.
2551 bool QQuickCanvas::clearBeforeRendering() const
2553 Q_D(const QQuickCanvas);
2554 return d->clearBeforeRendering;
2560 Creates a new QSGTexture from the supplied \a image. If the image has an
2561 alpha channel, the corresponding texture will have an alpha channel.
2563 The caller of the function is responsible for deleting the returned texture.
2564 The actual GL texture will be deleted when the texture object is deleted.
2566 Depending on the underlying implementation of the scene graph, the returned
2567 texture may be part of an atlas. For code to be portable across implementations
2568 one should always use the texture coordinates returned from
2569 QSGTexture::normalizedTextureSubRect() when building geometry.
2571 \warning This function will return 0 if the scene graph has not yet been
2574 \warning The returned texture is not memory managed by the scene graph and
2575 must be explicitely deleted by the caller on the rendering thread.
2576 This is acheived by deleting the texture from a QSGNode destructor
2577 or by using deleteLater() in the case where the texture already has affinity
2578 to the rendering thread.
2580 This function can be called from any thread.
2582 \sa sceneGraphInitialized()
2585 QSGTexture *QQuickCanvas::createTextureFromImage(const QImage &image) const
2587 Q_D(const QQuickCanvas);
2588 if (d->context && d->context->isReady())
2589 return d->context->createTexture(image);
2597 Creates a new QSGTexture object from an existing GL texture \a id.
2599 The caller of the function is responsible for deleting the returned texture.
2601 Use \a options to customize the texture attributes.
2603 \warning This function will return 0 if the scenegraph has not yet been
2606 \sa sceneGraphInitialized()
2608 QSGTexture *QQuickCanvas::createTextureFromId(uint id, const QSize &size, CreateTextureOptions options) const
2610 Q_D(const QQuickCanvas);
2611 if (d->context && d->context->isReady()) {
2612 QSGPlainTexture *texture = new QSGPlainTexture();
2613 texture->setTextureId(id);
2614 texture->setHasAlphaChannel(options & TextureHasAlphaChannel);
2615 texture->setHasMipmaps(options & TextureHasMipmaps);
2616 texture->setOwnsTexture(options & TextureOwnsGLTexture);
2617 texture->setTextureSize(size);
2625 Sets the color used to clear the opengl context to \a color.
2627 Setting the clear color has no effect when clearing is disabled.
2629 \sa setClearBeforeRendering()
2632 void QQuickCanvas::setClearColor(const QColor &color)
2635 if (color == d->clearColor)
2638 d->clearColor = color;
2639 emit clearColorChanged(color);
2645 Returns the color used to clear the opengl context.
2648 QColor QQuickCanvas::clearColor() const
2650 return d_func()->clearColor;
2655 #include "moc_qquickcanvas.cpp"