1 /****************************************************************************
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** 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"
48 #include <private/qsgrenderer_p.h>
49 #include <private/qsgflashnode_p.h>
51 #include <private/qguiapplication_p.h>
52 #include <QtGui/QInputPanel>
54 #include <private/qabstractanimation_p.h>
56 #include <QtGui/qpainter.h>
57 #include <QtGui/qevent.h>
58 #include <QtGui/qmatrix4x4.h>
59 #include <QtCore/qvarlengtharray.h>
60 #include <QtCore/qabstractanimation.h>
61 #include <QtDeclarative/qdeclarativeincubator.h>
63 #include <private/qdeclarativedebugtrace_p.h>
67 #define QQUICK_CANVAS_TIMING
68 #ifdef QQUICK_CANVAS_TIMING
69 static bool qquick_canvas_timing = !qgetenv("QML_CANVAS_TIMING").isEmpty();
70 static QTime threadTimer;
72 static int renderTime;
76 DEFINE_BOOL_CONFIG_OPTION(qmlFixedAnimationStep, QML_FIXED_ANIMATION_STEP)
77 DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP)
79 extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
81 void QQuickCanvasPrivate::updateFocusItemTransform()
84 QQuickItem *focus = q->activeFocusItem();
85 if (focus && qApp->inputPanel()->inputItem() == focus)
86 qApp->inputPanel()->setInputItemTransform(QQuickItemPrivate::get(focus)->itemToCanvasTransform());
89 class QQuickCanvasIncubationController : public QObject, public QDeclarativeIncubationController
92 QQuickCanvasIncubationController(QQuickCanvasPrivate *canvas)
93 : m_canvas(canvas), m_eventSent(false) {}
96 virtual bool event(QEvent *e)
98 if (e->type() == QEvent::User) {
99 Q_ASSERT(m_eventSent);
101 bool *amtp = m_canvas->thread->allowMainThreadProcessing();
102 while (incubatingObjectCount()) {
107 QCoreApplication::processEvents();
112 return QObject::event(e);
115 virtual void incubatingObjectCountChanged(int count)
117 if (count && !m_eventSent) {
119 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
124 QQuickCanvasPrivate *m_canvas;
128 class QQuickCanvasPlainRenderLoop : public QObject, public QQuickCanvasRenderLoop
131 QQuickCanvasPlainRenderLoop()
132 : updatePending(false)
133 , animationRunning(false)
135 qWarning("QQuickCanvas: using non-threaded render loop. Be very sure to not access scene graph "
136 "objects outside the QQuickItem::updatePaintNode() call. Failing to do so will cause "
137 "your code to crash on other platforms!");
140 virtual void paint() {
141 updatePending = false;
142 if (animationRunning && animationDriver())
143 animationDriver()->advance();
147 glViewport(0, 0, size.width(), size.height());
148 renderSceneGraph(size);
151 if (animationRunning)
155 virtual QImage grab() {
156 return qt_gl_read_framebuffer(size, false, false);
159 virtual void startRendering() {
163 initializeSceneGraph();
170 virtual void stopRendering() { }
172 virtual void maybeUpdate() {
173 if (!updatePending) {
174 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
175 updatePending = true;
179 virtual void animationStarted() {
180 animationRunning = true;
184 virtual void animationStopped() {
185 animationRunning = false;
188 virtual bool isRunning() const { return glContext(); } // Event loop is always running...
189 virtual void resize(const QSize &s) { size = s; }
190 virtual void setWindowSize(const QSize &s) { size = s; }
192 bool event(QEvent *e) {
193 if (e->type() == QEvent::User) {
197 return QObject::event(e);
202 uint updatePending : 1;
203 uint animationRunning : 1;
212 Prior to being added to a valid canvas items can set and clear focus with no
213 effect. Only once items are added to a canvas (by way of having a parent set that
214 already belongs to a canvas) do the focus rules apply. Focus goes back to
215 having no effect if an item is removed from a canvas.
217 When an item is moved into a new focus scope (either being added to a canvas
218 for the first time, or having its parent changed), if the focus scope already has
219 a scope focused item that takes precedence over the item being added. Otherwise,
220 the focus of the added tree is used. In the case of of a tree of items being
221 added to a canvas for the first time, which may have a conflicted focus state (two
222 or more items in one scope having focus set), the same rule is applied item by item -
223 thus the first item that has focus will get it (assuming the scope doesn't already
224 have a scope focused item), and the other items will have their focus cleared.
231 The threaded rendering uses a number of different variables to track potential
232 states used to handle resizing, initial paint, grabbing and driving animations
233 while ALWAYS keeping the GL context in the rendering thread and keeping the
234 overhead of normal one-shot paints and vblank driven animations at a minimum.
236 Resize, initial show and grab suffer slightly in this model as they are locked
237 to the rendering in the rendering thread, but this is a necessary evil for
240 Variables that are used:
242 Private::animationRunning: This is true while the animations are running, and only
243 written to inside locks.
245 RenderThread::isGuiBlocked: This is used to indicate that the GUI thread owns the
246 lock. This variable is an integer to allow for recursive calls to lockInGui()
247 without using a recursive mutex. See isGuiBlockPending.
249 RenderThread::isPaintComplete: This variable is cleared when rendering starts and
250 set once rendering is complete. It is monitored in the paintEvent(),
251 resizeEvent() and grab() functions to force them to wait for rendering to
254 RenderThread::isGuiBlockPending: This variable is set in the render thread just
255 before the sync event is sent to the GUI thread. It is used to avoid deadlocks
256 in the case where render thread waits while waiting for GUI to pick up the sync
257 event and GUI thread gets a resizeEvent, the initial paintEvent or a grab.
258 When this happens, we use the
259 exhaustSyncEvent() function to do the sync right there and mark the coming
260 sync event to be discarded. There can only ever be one sync incoming.
262 RenderThread::isRenderBlock: This variable is true when animations are not
263 running and the render thread has gone to sleep, waiting for more to do.
265 RenderThread::isExternalUpdatePending: This variable is set to false during
266 the sync phase in the GUI thread and to true in maybeUpdate(). It is an
267 indication to the render thread that another render pass needs to take
268 place, rather than the render thread going to sleep after completing its swap.
270 RenderThread::doGrab: This variable is set by the grab() function and
271 tells the renderer to do a grab after rendering is complete and before
274 RenderThread::shouldExit: This variable is used to determine if the render
275 thread should do a nother pass. It is typically set as a result of show()
276 and unset as a result of hide() or during shutdown()
278 RenderThread::hasExited: Used by the GUI thread to synchronize the shutdown
279 after shouldExit has been set to true.
282 // #define FOCUS_DEBUG
283 // #define MOUSE_DEBUG
284 // #define TOUCH_DEBUG
285 // #define DIRTY_DEBUG
286 // #define THREAD_DEBUG
288 // #define FRAME_TIMING
291 static QTime frameTimer;
292 int sceneGraphRenderTime;
296 QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData()
301 QQuickRootItem::QQuickRootItem()
305 void QQuickCanvas::exposeEvent(QExposeEvent *)
311 void QQuickCanvas::resizeEvent(QResizeEvent *)
314 d->thread->resize(size());
317 void QQuickCanvas::animationStarted()
319 d_func()->thread->animationStarted();
322 void QQuickCanvas::animationStopped()
324 d_func()->thread->animationStopped();
327 void QQuickCanvas::showEvent(QShowEvent *)
330 if (d->vsyncAnimations) {
331 if (!d->animationDriver) {
332 d->animationDriver = d->context->createAnimationDriver(this);
333 connect(d->animationDriver, SIGNAL(started()), this, SLOT(animationStarted()), Qt::DirectConnection);
334 connect(d->animationDriver, SIGNAL(stopped()), this, SLOT(animationStopped()), Qt::DirectConnection);
336 d->animationDriver->install();
339 if (!d->thread->isRunning()) {
340 d->thread->setWindowSize(size());
341 d->thread->startRendering();
345 void QQuickCanvas::hideEvent(QHideEvent *)
348 d->thread->stopRendering();
354 Sets weither this canvas should use vsync driven animations.
356 This option can only be set on one single QQuickCanvas, and that it's
357 vsync signal will then be used to drive all animations in the
360 This feature is primarily useful for single QQuickCanvas, QML-only
363 \warning Enabling vsync on multiple QQuickCanvas instances has
366 void QQuickCanvas::setVSyncAnimations(bool enabled)
370 qWarning("QQuickCanvas::setVSyncAnimations: Cannot be changed when widget is shown");
373 d->vsyncAnimations = enabled;
379 Returns true if this canvas should use vsync driven animations;
380 otherwise returns false.
382 bool QQuickCanvas::vsyncAnimations() const
384 Q_D(const QQuickCanvas);
385 return d->vsyncAnimations;
388 void QQuickCanvasPrivate::initializeSceneGraph()
391 context = QSGContext::createDefaultContext();
393 if (context->isReady())
396 QOpenGLContext *glctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
397 context->initialize(glctx);
400 QObject::connect(context->renderer(), SIGNAL(sceneGraphChanged()), q, SLOT(maybeUpdate()),
401 Qt::DirectConnection);
403 if (!QQuickItemPrivate::get(rootItem)->itemNode()->parent()) {
404 context->rootNode()->appendChildNode(QQuickItemPrivate::get(rootItem)->itemNode());
407 emit q_func()->sceneGraphInitialized();
410 void QQuickCanvasPrivate::polishItems()
412 while (!itemsToPolish.isEmpty()) {
413 QSet<QQuickItem *>::Iterator iter = itemsToPolish.begin();
414 QQuickItem *item = *iter;
415 itemsToPolish.erase(iter);
416 QQuickItemPrivate::get(item)->polishScheduled = false;
417 item->updatePolish();
419 updateFocusItemTransform();
423 void QQuickCanvasPrivate::syncSceneGraph()
429 void QQuickCanvasPrivate::renderSceneGraph(const QSize &size)
431 context->renderer()->setDeviceRect(QRect(QPoint(0, 0), size));
432 context->renderer()->setViewportRect(QRect(QPoint(0, 0), renderTarget ? renderTarget->size() : size));
433 context->renderer()->setProjectionMatrixToDeviceRect();
435 context->renderNextFrame(renderTarget);
438 sceneGraphRenderTime = frameTimer.elapsed();
443 // glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel);
444 readbackTime = frameTimer.elapsed();
449 // ### Do we need this?
450 void QQuickCanvas::sceneGraphChanged()
452 // Q_D(QQuickCanvas);
453 // d->needsRepaint = true;
456 QQuickCanvasPrivate::QQuickCanvasPrivate()
459 , mouseGrabberItem(0)
462 , vsyncAnimations(false)
466 , incubationController(0)
470 QQuickCanvasPrivate::~QQuickCanvasPrivate()
474 void QQuickCanvasPrivate::init(QQuickCanvas *c)
476 QUnifiedTimer::instance(true)->setConsistentTiming(qmlFixedAnimationStep());
482 rootItem = new QQuickRootItem;
483 QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(rootItem);
484 rootItemPrivate->canvas = q;
485 rootItemPrivate->flags |= QQuickItem::ItemIsFocusScope;
487 // QML always has focus. It is important that this call happens after the rootItem
489 rootItem->setFocus(true);
491 bool threaded = !qmlNoThreadedRenderer();
493 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL)) {
494 qWarning("QQuickCanvas: platform does not support threaded rendering!");
499 thread = new QQuickCanvasRenderThread();
501 thread = new QQuickCanvasPlainRenderLoop();
503 thread->renderer = q;
506 context = QSGContext::createDefaultContext();
507 thread->moveContextToThread(context);
509 q->setSurfaceType(QWindow::OpenGLSurface);
510 q->setFormat(context->defaultSurfaceFormat());
513 void QQuickCanvasPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform)
515 for (int i=0; i<touchPoints.count(); i++) {
516 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
517 touchPoint.setRect(transform.mapRect(touchPoint.sceneRect()));
518 touchPoint.setStartPos(transform.map(touchPoint.startScenePos()));
519 touchPoint.setLastPos(transform.map(touchPoint.lastScenePos()));
525 Translates the data in \a touchEvent to this canvas. This method leaves the item local positions in
526 \a touchEvent untouched (these are filled in later).
528 void QQuickCanvasPrivate::translateTouchEvent(QTouchEvent *touchEvent)
530 // Q_Q(QQuickCanvas);
532 // touchEvent->setWidget(q); // ### refactor...
534 QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
535 for (int i = 0; i < touchPoints.count(); ++i) {
536 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
538 touchPoint.setScreenRect(touchPoint.sceneRect());
539 touchPoint.setStartScreenPos(touchPoint.startScenePos());
540 touchPoint.setLastScreenPos(touchPoint.lastScenePos());
542 touchPoint.setSceneRect(touchPoint.rect());
543 touchPoint.setStartScenePos(touchPoint.startPos());
544 touchPoint.setLastScenePos(touchPoint.lastPos());
546 if (touchPoint.isPrimary())
547 lastMousePosition = touchPoint.pos().toPoint();
549 touchEvent->setTouchPoints(touchPoints);
552 void QQuickCanvasPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
557 Q_ASSERT(scope || item == rootItem);
560 qWarning() << "QQuickCanvasPrivate::setFocusInScope():";
561 qWarning() << " scope:" << (QObject *)scope;
563 qWarning() << " scopeSubFocusItem:" << (QObject *)QQuickItemPrivate::get(scope)->subFocusItem;
564 qWarning() << " item:" << (QObject *)item;
565 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
568 QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0;
569 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
571 QQuickItem *oldActiveFocusItem = 0;
572 QQuickItem *newActiveFocusItem = 0;
574 QVarLengthArray<QQuickItem *, 20> changed;
576 // Does this change the active focus?
577 if (item == rootItem || scopePrivate->activeFocus) {
578 oldActiveFocusItem = activeFocusItem;
579 newActiveFocusItem = item;
580 while (newActiveFocusItem->isFocusScope() && newActiveFocusItem->scopedFocusItem())
581 newActiveFocusItem = newActiveFocusItem->scopedFocusItem();
583 if (oldActiveFocusItem) {
585 qApp->inputPanel()->commit();
589 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
590 q->sendEvent(oldActiveFocusItem, &event);
592 QQuickItem *afi = oldActiveFocusItem;
593 while (afi != scope) {
594 if (QQuickItemPrivate::get(afi)->activeFocus) {
595 QQuickItemPrivate::get(afi)->activeFocus = false;
598 afi = afi->parentItem();
603 if (item != rootItem) {
604 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
605 // Correct focus chain in scope
606 if (oldSubFocusItem) {
607 QQuickItem *sfi = scopePrivate->subFocusItem->parentItem();
608 while (sfi != scope) {
609 QQuickItemPrivate::get(sfi)->subFocusItem = 0;
610 sfi = sfi->parentItem();
614 scopePrivate->subFocusItem = item;
615 QQuickItem *sfi = scopePrivate->subFocusItem->parentItem();
616 while (sfi != scope) {
617 QQuickItemPrivate::get(sfi)->subFocusItem = item;
618 sfi = sfi->parentItem();
622 if (oldSubFocusItem) {
623 QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
624 changed << oldSubFocusItem;
628 if (!(options & DontChangeFocusProperty)) {
629 // if (item != rootItem || q->hasFocus()) { // ### refactor: focus handling...
630 itemPrivate->focus = true;
635 if (newActiveFocusItem) { // ### refactor: && q->hasFocus()) {
636 activeFocusItem = newActiveFocusItem;
638 QQuickItemPrivate::get(newActiveFocusItem)->activeFocus = true;
639 changed << newActiveFocusItem;
641 QQuickItem *afi = newActiveFocusItem->parentItem();
642 while (afi && afi != scope) {
643 if (afi->isFocusScope()) {
644 QQuickItemPrivate::get(afi)->activeFocus = true;
647 afi = afi->parentItem();
650 updateInputMethodData();
652 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
653 q->sendEvent(newActiveFocusItem, &event);
655 updateInputMethodData();
658 if (!changed.isEmpty())
659 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
662 void QQuickCanvasPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
668 Q_ASSERT(scope || item == rootItem);
671 qWarning() << "QQuickCanvasPrivate::clearFocusInScope():";
672 qWarning() << " scope:" << (QObject *)scope;
673 qWarning() << " item:" << (QObject *)item;
674 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
677 QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0;
679 QQuickItem *oldActiveFocusItem = 0;
680 QQuickItem *newActiveFocusItem = 0;
682 QVarLengthArray<QQuickItem *, 20> changed;
684 Q_ASSERT(item == rootItem || item == scopePrivate->subFocusItem);
686 // Does this change the active focus?
687 if (item == rootItem || scopePrivate->activeFocus) {
688 oldActiveFocusItem = activeFocusItem;
689 newActiveFocusItem = scope;
691 Q_ASSERT(oldActiveFocusItem);
694 qApp->inputPanel()->commit();
698 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
699 q->sendEvent(oldActiveFocusItem, &event);
701 QQuickItem *afi = oldActiveFocusItem;
702 while (afi != scope) {
703 if (QQuickItemPrivate::get(afi)->activeFocus) {
704 QQuickItemPrivate::get(afi)->activeFocus = false;
707 afi = afi->parentItem();
711 if (item != rootItem) {
712 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
713 // Correct focus chain in scope
714 if (oldSubFocusItem) {
715 QQuickItem *sfi = scopePrivate->subFocusItem->parentItem();
716 while (sfi != scope) {
717 QQuickItemPrivate::get(sfi)->subFocusItem = 0;
718 sfi = sfi->parentItem();
721 scopePrivate->subFocusItem = 0;
723 if (oldSubFocusItem && !(options & DontChangeFocusProperty)) {
724 QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
725 changed << oldSubFocusItem;
727 } else if (!(options & DontChangeFocusProperty)) {
728 QQuickItemPrivate::get(item)->focus = false;
732 if (newActiveFocusItem) {
733 Q_ASSERT(newActiveFocusItem == scope);
734 activeFocusItem = scope;
736 updateInputMethodData();
738 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
739 q->sendEvent(newActiveFocusItem, &event);
741 updateInputMethodData();
744 if (!changed.isEmpty())
745 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
748 void QQuickCanvasPrivate::notifyFocusChangesRecur(QQuickItem **items, int remaining)
750 QDeclarativeGuard<QQuickItem> item(*items);
753 notifyFocusChangesRecur(items + 1, remaining - 1);
756 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
758 if (itemPrivate->notifiedFocus != itemPrivate->focus) {
759 itemPrivate->notifiedFocus = itemPrivate->focus;
760 emit item->focusChanged(itemPrivate->focus);
763 if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) {
764 itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus;
765 itemPrivate->itemChange(QQuickItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus);
766 emit item->activeFocusChanged(itemPrivate->activeFocus);
771 void QQuickCanvasPrivate::updateInputMethodData()
773 QQuickItem *inputItem = 0;
774 if (activeFocusItem && activeFocusItem->flags() & QQuickItem::ItemAcceptsInputMethod)
775 inputItem = activeFocusItem;
776 qApp->inputPanel()->setInputItem(inputItem);
779 QVariant QQuickCanvas::inputMethodQuery(Qt::InputMethodQuery query) const
781 Q_D(const QQuickCanvas);
782 if (!d->activeFocusItem || !(QQuickItemPrivate::get(d->activeFocusItem)->flags & QQuickItem::ItemAcceptsInputMethod))
784 QVariant value = d->activeFocusItem->inputMethodQuery(query);
787 QVariant::Type type = value.type();
788 if (type == QVariant::RectF || type == QVariant::Rect) {
789 const QTransform transform = QQuickItemPrivate::get(d->activeFocusItem)->itemToCanvasTransform();
790 value = transform.mapRect(value.toRectF());
791 } else if (type == QVariant::PointF || type == QVariant::Point) {
792 const QTransform transform = QQuickItemPrivate::get(d->activeFocusItem)->itemToCanvasTransform();
793 value = transform.map(value.toPointF());
798 void QQuickCanvasPrivate::dirtyItem(QQuickItem *)
804 void QQuickCanvasPrivate::cleanup(QSGNode *n)
808 Q_ASSERT(!cleanupNodeList.contains(n));
809 cleanupNodeList.append(n);
814 QQuickCanvas::QQuickCanvas(QWindow *parent)
815 : QWindow(*(new QQuickCanvasPrivate), parent)
821 QQuickCanvas::QQuickCanvas(QQuickCanvasPrivate &dd, QWindow *parent)
822 : QWindow(dd, parent)
828 QQuickCanvas::~QQuickCanvas()
832 if (d->thread->isRunning()) {
833 d->thread->stopRendering();
838 // ### should we change ~QQuickItem to handle this better?
839 // manually cleanup for the root item (item destructor only handles these when an item is parented)
840 QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(d->rootItem);
841 rootItemPrivate->removeFromDirtyList();
843 delete d->incubationController; d->incubationController = 0;
845 delete d->rootItem; d->rootItem = 0;
849 QQuickItem *QQuickCanvas::rootItem() const
851 Q_D(const QQuickCanvas);
856 QQuickItem *QQuickCanvas::activeFocusItem() const
858 Q_D(const QQuickCanvas);
860 return d->activeFocusItem;
863 QQuickItem *QQuickCanvas::mouseGrabberItem() const
865 Q_D(const QQuickCanvas);
867 return d->mouseGrabberItem;
871 bool QQuickCanvasPrivate::clearHover()
873 if (hoverItems.isEmpty())
876 QPointF pos = QCursor::pos(); // ### refactor: q->mapFromGlobal(QCursor::pos());
878 bool accepted = false;
879 foreach (QQuickItem* item, hoverItems)
880 accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), true) || accepted;
886 bool QQuickCanvas::event(QEvent *e)
892 case QEvent::TouchBegin:
893 case QEvent::TouchUpdate:
894 case QEvent::TouchEnd:
896 QTouchEvent *touch = static_cast<QTouchEvent *>(e);
897 d->translateTouchEvent(touch);
898 d->deliverTouchEvent(touch);
899 if (!touch->isAccepted())
905 d->lastMousePosition = QPoint();
907 case QEvent::DragEnter:
908 case QEvent::DragLeave:
909 case QEvent::DragMove:
911 d->deliverDragEvent(&d->dragGrabber, e);
913 case QEvent::WindowDeactivate:
914 rootItem()->windowDeactivateEvent();
920 return QWindow::event(e);
923 void QQuickCanvas::keyPressEvent(QKeyEvent *e)
927 if (d->activeFocusItem)
928 sendEvent(d->activeFocusItem, e);
931 void QQuickCanvas::keyReleaseEvent(QKeyEvent *e)
935 if (d->activeFocusItem)
936 sendEvent(d->activeFocusItem, e);
939 void QQuickCanvas::inputMethodEvent(QInputMethodEvent *e)
943 if (d->activeFocusItem)
944 sendEvent(d->activeFocusItem, e);
947 bool QQuickCanvasPrivate::deliverInitialMousePressEvent(QQuickItem *item, QMouseEvent *event)
951 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
952 if (itemPrivate->opacity == 0.0)
955 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
956 QPointF p = item->mapFromScene(event->windowPos());
957 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
961 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
962 for (int ii = children.count() - 1; ii >= 0; --ii) {
963 QQuickItem *child = children.at(ii);
964 if (!child->isVisible() || !child->isEnabled())
966 if (deliverInitialMousePressEvent(child, event))
970 if (itemPrivate->acceptedMouseButtons & event->button()) {
971 QPointF p = item->mapFromScene(event->windowPos());
972 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
973 QMouseEvent me(event->type(), p, event->windowPos(), event->screenPos(),
974 event->button(), event->buttons(), event->modifiers());
976 mouseGrabberItem = item;
977 q->sendEvent(item, &me);
978 event->setAccepted(me.isAccepted());
981 mouseGrabberItem->ungrabMouse();
982 mouseGrabberItem = 0;
989 bool QQuickCanvasPrivate::deliverMouseEvent(QMouseEvent *event)
993 lastMousePosition = event->windowPos();
995 if (!mouseGrabberItem &&
996 event->type() == QEvent::MouseButtonPress &&
997 (event->button() & event->buttons()) == event->buttons()) {
998 return deliverInitialMousePressEvent(rootItem, event);
1001 if (mouseGrabberItem) {
1002 QQuickItemPrivate *mgPrivate = QQuickItemPrivate::get(mouseGrabberItem);
1003 const QTransform &transform = mgPrivate->canvasToItemTransform();
1004 QMouseEvent me(event->type(), transform.map(event->windowPos()), event->windowPos(), event->screenPos(),
1005 event->button(), event->buttons(), event->modifiers());
1007 q->sendEvent(mouseGrabberItem, &me);
1008 event->setAccepted(me.isAccepted());
1009 if (me.isAccepted())
1016 void QQuickCanvas::mousePressEvent(QMouseEvent *event)
1021 qWarning() << "QQuickCanvas::mousePressEvent()" << event->pos() << event->button() << event->buttons();
1024 d->deliverMouseEvent(event);
1027 void QQuickCanvas::mouseReleaseEvent(QMouseEvent *event)
1032 qWarning() << "QQuickCanvas::mouseReleaseEvent()" << event->pos() << event->button() << event->buttons();
1035 if (!d->mouseGrabberItem) {
1036 QWindow::mouseReleaseEvent(event);
1040 d->deliverMouseEvent(event);
1041 d->mouseGrabberItem = 0;
1044 void QQuickCanvas::mouseDoubleClickEvent(QMouseEvent *event)
1049 qWarning() << "QQuickCanvas::mouseDoubleClickEvent()" << event->pos() << event->button() << event->buttons();
1052 if (!d->mouseGrabberItem && (event->button() & event->buttons()) == event->buttons()) {
1053 if (d->deliverInitialMousePressEvent(d->rootItem, event))
1060 d->deliverMouseEvent(event);
1063 bool QQuickCanvasPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
1064 const QPointF &scenePos, const QPointF &lastScenePos,
1065 Qt::KeyboardModifiers modifiers, bool accepted)
1068 const QTransform transform = QQuickItemPrivate::get(item)->canvasToItemTransform();
1070 //create copy of event
1071 QHoverEvent hoverEvent(type, transform.map(scenePos), transform.map(lastScenePos), modifiers);
1072 hoverEvent.setAccepted(accepted);
1074 q->sendEvent(item, &hoverEvent);
1076 return hoverEvent.isAccepted();
1079 void QQuickCanvas::mouseMoveEvent(QMouseEvent *event)
1084 qWarning() << "QQuickCanvas::mouseMoveEvent()" << event->pos() << event->button() << event->buttons();
1087 if (!d->mouseGrabberItem) {
1088 if (d->lastMousePosition.isNull())
1089 d->lastMousePosition = event->windowPos();
1090 QPointF last = d->lastMousePosition;
1091 d->lastMousePosition = event->windowPos();
1093 bool accepted = event->isAccepted();
1094 bool delivered = d->deliverHoverEvent(d->rootItem, event->windowPos(), last, event->modifiers(), accepted);
1096 //take care of any exits
1097 accepted = d->clearHover();
1099 event->setAccepted(accepted);
1103 d->deliverMouseEvent(event);
1106 bool QQuickCanvasPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
1107 Qt::KeyboardModifiers modifiers, bool &accepted)
1109 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1110 if (itemPrivate->opacity == 0.0)
1113 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1114 QPointF p = item->mapFromScene(scenePos);
1115 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
1119 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1120 for (int ii = children.count() - 1; ii >= 0; --ii) {
1121 QQuickItem *child = children.at(ii);
1122 if (!child->isVisible() || !child->isEnabled())
1124 if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, accepted))
1128 if (itemPrivate->hoverEnabled) {
1129 QPointF p = item->mapFromScene(scenePos);
1130 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1131 if (!hoverItems.isEmpty() && hoverItems[0] == item) {
1133 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1135 QList<QQuickItem *> itemsToHover;
1136 QQuickItem* parent = item;
1137 itemsToHover << item;
1138 while ((parent = parent->parentItem()))
1139 itemsToHover << parent;
1141 // Leaving from previous hovered items until we reach the item or one of its ancestors.
1142 while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems[0])) {
1143 sendHoverEvent(QEvent::HoverLeave, hoverItems[0], scenePos, lastScenePos, modifiers, accepted);
1144 hoverItems.removeFirst();
1147 if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item
1148 // ### Shouldn't we send moves for the parent items as well?
1149 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1151 // Enter items that are not entered yet.
1153 if (!hoverItems.isEmpty())
1154 startIdx = itemsToHover.indexOf(hoverItems[0]) - 1;
1156 startIdx = itemsToHover.count() - 1;
1158 for (int i = startIdx; i >= 0; i--) {
1159 QQuickItem *itemToHover = itemsToHover[i];
1160 if (QQuickItemPrivate::get(itemToHover)->hoverEnabled) {
1161 hoverItems.prepend(itemToHover);
1162 sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, accepted);
1174 bool QQuickCanvasPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event)
1177 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1178 if (itemPrivate->opacity == 0.0)
1181 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1182 QPointF p = item->mapFromScene(event->posF());
1183 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
1187 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1188 for (int ii = children.count() - 1; ii >= 0; --ii) {
1189 QQuickItem *child = children.at(ii);
1190 if (!child->isVisible() || !child->isEnabled())
1192 if (deliverWheelEvent(child, event))
1196 QPointF p = item->mapFromScene(event->posF());
1197 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1198 QWheelEvent wheel(p, event->delta(), event->buttons(), event->modifiers(), event->orientation());
1200 q->sendEvent(item, &wheel);
1201 if (wheel.isAccepted()) {
1210 #ifndef QT_NO_WHEELEVENT
1211 void QQuickCanvas::wheelEvent(QWheelEvent *event)
1215 qWarning() << "QQuickCanvas::wheelEvent()" << event->pos() << event->delta() << event->orientation();
1218 d->deliverWheelEvent(d->rootItem, event);
1220 #endif // QT_NO_WHEELEVENT
1222 bool QQuickCanvasPrivate::deliverTouchEvent(QTouchEvent *event)
1225 if (event->type() == QEvent::TouchBegin)
1226 qWarning("touchBeginEvent");
1227 else if (event->type() == QEvent::TouchUpdate)
1228 qWarning("touchUpdateEvent");
1229 else if (event->type() == QEvent::TouchEnd)
1230 qWarning("touchEndEvent");
1233 QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > updatedPoints;
1235 if (event->type() == QTouchEvent::TouchBegin) { // all points are new touch points
1236 QSet<int> acceptedNewPoints;
1237 deliverTouchPoints(rootItem, event, event->touchPoints(), &acceptedNewPoints, &updatedPoints);
1238 if (acceptedNewPoints.count() > 0)
1240 return event->isAccepted();
1243 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
1244 QList<QTouchEvent::TouchPoint> newPoints;
1245 QQuickItem *item = 0;
1246 for (int i=0; i<touchPoints.count(); i++) {
1247 const QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
1248 switch (touchPoint.state()) {
1249 case Qt::TouchPointPressed:
1250 newPoints << touchPoint;
1252 case Qt::TouchPointMoved:
1253 case Qt::TouchPointStationary:
1254 case Qt::TouchPointReleased:
1255 if (itemForTouchPointId.contains(touchPoint.id())) {
1256 item = itemForTouchPointId[touchPoint.id()];
1258 updatedPoints[item].append(touchPoint);
1266 if (newPoints.count() > 0 || updatedPoints.count() > 0) {
1267 QSet<int> acceptedNewPoints;
1268 int prevCount = updatedPoints.count();
1269 deliverTouchPoints(rootItem, event, newPoints, &acceptedNewPoints, &updatedPoints);
1270 if (acceptedNewPoints.count() > 0 || updatedPoints.count() != prevCount)
1274 if (event->touchPointStates() & Qt::TouchPointReleased) {
1275 for (int i=0; i<touchPoints.count(); i++) {
1276 if (touchPoints[i].state() == Qt::TouchPointReleased)
1277 itemForTouchPointId.remove(touchPoints[i].id());
1281 return event->isAccepted();
1284 bool QQuickCanvasPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints, QSet<int> *acceptedNewPoints, QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *updatedPoints)
1287 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1289 if (itemPrivate->opacity == 0.0)
1292 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1293 QRectF bounds(0, 0, item->width(), item->height());
1294 for (int i=0; i<newPoints.count(); i++) {
1295 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1296 if (!bounds.contains(p))
1301 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1302 for (int ii = children.count() - 1; ii >= 0; --ii) {
1303 QQuickItem *child = children.at(ii);
1304 if (!child->isEnabled())
1306 if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints))
1310 QList<QTouchEvent::TouchPoint> matchingPoints;
1311 if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) {
1312 QRectF bounds(0, 0, item->width(), item->height());
1313 for (int i=0; i<newPoints.count(); i++) {
1314 if (acceptedNewPoints->contains(newPoints[i].id()))
1316 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1317 if (bounds.contains(p))
1318 matchingPoints << newPoints[i];
1322 if (matchingPoints.count() > 0 || (*updatedPoints)[item].count() > 0) {
1323 QList<QTouchEvent::TouchPoint> &eventPoints = (*updatedPoints)[item];
1324 eventPoints.append(matchingPoints);
1325 transformTouchPoints(eventPoints, itemPrivate->canvasToItemTransform());
1327 Qt::TouchPointStates eventStates;
1328 for (int i=0; i<eventPoints.count(); i++)
1329 eventStates |= eventPoints[i].state();
1330 // if all points have the same state, set the event type accordingly
1331 QEvent::Type eventType;
1332 switch (eventStates) {
1333 case Qt::TouchPointPressed:
1334 eventType = QEvent::TouchBegin;
1336 case Qt::TouchPointReleased:
1337 eventType = QEvent::TouchEnd;
1340 eventType = QEvent::TouchUpdate;
1344 if (eventStates != Qt::TouchPointStationary) {
1345 QTouchEvent touchEvent(eventType);
1346 // touchEvent.setWidget(q); // ### refactor: what is the consequence of not setting the widget?
1347 touchEvent.setDeviceType(event->deviceType());
1348 touchEvent.setModifiers(event->modifiers());
1349 touchEvent.setTouchPointStates(eventStates);
1350 touchEvent.setTouchPoints(eventPoints);
1351 touchEvent.setTimestamp(event->timestamp());
1353 touchEvent.accept();
1354 q->sendEvent(item, &touchEvent);
1356 if (touchEvent.isAccepted()) {
1357 for (int i=0; i<matchingPoints.count(); i++) {
1358 itemForTouchPointId[matchingPoints[i].id()] = item;
1359 acceptedNewPoints->insert(matchingPoints[i].id());
1365 updatedPoints->remove(item);
1366 if (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty())
1372 void QQuickCanvasPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event)
1375 grabber->resetTarget();
1376 QQuickDragGrabber::iterator grabItem = grabber->begin();
1377 if (grabItem != grabber->end()) {
1378 Q_ASSERT(event->type() != QEvent::DragEnter);
1379 if (event->type() == QEvent::Drop) {
1380 QDropEvent *e = static_cast<QDropEvent *>(event);
1381 for (e->setAccepted(false); !e->isAccepted() && grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1382 QPointF p = (**grabItem)->mapFromScene(e->pos());
1383 QDropEvent translatedEvent(
1385 e->possibleActions(),
1388 e->keyboardModifiers());
1389 QQuickDropEventEx::copyActions(&translatedEvent, *e);
1390 q->sendEvent(**grabItem, &translatedEvent);
1391 e->setAccepted(translatedEvent.isAccepted());
1392 e->setDropAction(translatedEvent.dropAction());
1393 grabber->setTarget(**grabItem);
1396 if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave.
1397 QDragLeaveEvent leaveEvent;
1398 for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
1399 q->sendEvent(**grabItem, &leaveEvent);
1401 } else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1402 QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event);
1403 if (deliverDragEvent(grabber, **grabItem, moveEvent)) {
1404 moveEvent->setAccepted(true);
1405 for (++grabItem; grabItem != grabber->end();) {
1406 QPointF p = (**grabItem)->mapFromScene(moveEvent->pos());
1407 if (QRectF(0, 0, (**grabItem)->width(), (**grabItem)->height()).contains(p)) {
1408 QDragMoveEvent translatedEvent(
1410 moveEvent->possibleActions(),
1411 moveEvent->mimeData(),
1412 moveEvent->mouseButtons(),
1413 moveEvent->keyboardModifiers());
1414 QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent);
1415 q->sendEvent(**grabItem, &translatedEvent);
1418 QDragLeaveEvent leaveEvent;
1419 q->sendEvent(**grabItem, &leaveEvent);
1420 grabItem = grabber->release(grabItem);
1425 QDragLeaveEvent leaveEvent;
1426 q->sendEvent(**grabItem, &leaveEvent);
1430 if (event->type() == QEvent::DragEnter || event->type() == QEvent::DragMove) {
1431 QDragMoveEvent *e = static_cast<QDragMoveEvent *>(event);
1432 QDragEnterEvent enterEvent(
1434 e->possibleActions(),
1437 e->keyboardModifiers());
1438 QQuickDropEventEx::copyActions(&enterEvent, *e);
1439 event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent));
1443 bool QQuickCanvasPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event)
1446 bool accepted = false;
1447 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1448 if (itemPrivate->opacity == 0.0 || !item->isVisible() || !item->isEnabled())
1451 QPointF p = item->mapFromScene(event->pos());
1452 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1453 if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) {
1454 QDragMoveEvent translatedEvent(
1456 event->possibleActions(),
1458 event->mouseButtons(),
1459 event->keyboardModifiers(),
1461 QQuickDropEventEx::copyActions(&translatedEvent, *event);
1462 q->sendEvent(item, &translatedEvent);
1463 if (event->type() == QEvent::DragEnter) {
1464 if (translatedEvent.isAccepted()) {
1465 grabber->grab(item);
1472 } else if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1476 QDragEnterEvent enterEvent(
1478 event->possibleActions(),
1480 event->mouseButtons(),
1481 event->keyboardModifiers());
1482 QQuickDropEventEx::copyActions(&enterEvent, *event);
1483 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1484 for (int ii = children.count() - 1; ii >= 0; --ii) {
1485 if (deliverDragEvent(grabber, children.at(ii), &enterEvent))
1492 bool QQuickCanvasPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QMouseEvent *event)
1497 QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target);
1498 if (targetPrivate->filtersChildMouseEvents)
1499 if (target->childMouseEventFilter(item, event))
1502 if (sendFilteredMouseEvent(target->parentItem(), item, event))
1508 bool QQuickCanvas::sendEvent(QQuickItem *item, QEvent *e)
1513 qWarning("QQuickCanvas::sendEvent: Cannot send event to a null item");
1519 switch (e->type()) {
1520 case QEvent::KeyPress:
1521 case QEvent::KeyRelease:
1523 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1524 while (!e->isAccepted() && (item = item->parentItem())) {
1526 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1529 case QEvent::InputMethod:
1531 QQuickItemPrivate::get(item)->deliverInputMethodEvent(static_cast<QInputMethodEvent *>(e));
1532 while (!e->isAccepted() && (item = item->parentItem())) {
1534 QQuickItemPrivate::get(item)->deliverInputMethodEvent(static_cast<QInputMethodEvent *>(e));
1537 case QEvent::FocusIn:
1538 case QEvent::FocusOut:
1539 QQuickItemPrivate::get(item)->deliverFocusEvent(static_cast<QFocusEvent *>(e));
1541 case QEvent::MouseButtonPress:
1542 case QEvent::MouseButtonRelease:
1543 case QEvent::MouseButtonDblClick:
1544 case QEvent::MouseMove:
1545 // XXX todo - should sendEvent be doing this? how does it relate to forwarded events?
1547 QMouseEvent *se = static_cast<QMouseEvent *>(e);
1548 if (!d->sendFilteredMouseEvent(item->parentItem(), item, se)) {
1550 QQuickItemPrivate::get(item)->deliverMouseEvent(se);
1555 QQuickItemPrivate::get(item)->deliverWheelEvent(static_cast<QWheelEvent *>(e));
1557 case QEvent::HoverEnter:
1558 case QEvent::HoverLeave:
1559 case QEvent::HoverMove:
1560 QQuickItemPrivate::get(item)->deliverHoverEvent(static_cast<QHoverEvent *>(e));
1562 case QEvent::TouchBegin:
1563 case QEvent::TouchUpdate:
1564 case QEvent::TouchEnd:
1565 QQuickItemPrivate::get(item)->deliverTouchEvent(static_cast<QTouchEvent *>(e));
1567 case QEvent::DragEnter:
1568 case QEvent::DragMove:
1569 case QEvent::DragLeave:
1571 QQuickItemPrivate::get(item)->deliverDragEvent(e);
1580 void QQuickCanvasPrivate::cleanupNodes()
1582 for (int ii = 0; ii < cleanupNodeList.count(); ++ii)
1583 delete cleanupNodeList.at(ii);
1584 cleanupNodeList.clear();
1587 void QQuickCanvasPrivate::updateDirtyNodes()
1590 qWarning() << "QQuickCanvasPrivate::updateDirtyNodes():";
1595 QQuickItem *updateList = dirtyItemList;
1597 if (updateList) QQuickItemPrivate::get(updateList)->prevDirtyItem = &updateList;
1599 while (updateList) {
1600 QQuickItem *item = updateList;
1601 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
1602 itemPriv->removeFromDirtyList();
1605 qWarning() << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString());
1607 updateDirtyNode(item);
1611 void QQuickCanvasPrivate::updateDirtyNode(QQuickItem *item)
1613 #ifdef QML_RUNTIME_TESTING
1614 bool didFlash = false;
1617 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
1618 quint32 dirty = itemPriv->dirtyAttributes;
1619 itemPriv->dirtyAttributes = 0;
1621 if ((dirty & QQuickItemPrivate::TransformUpdateMask) ||
1622 (dirty & QQuickItemPrivate::Size && itemPriv->origin != QQuickItem::TopLeft &&
1623 (itemPriv->scale != 1. || itemPriv->rotation != 0.))) {
1627 if (itemPriv->x != 0. || itemPriv->y != 0.)
1628 matrix.translate(itemPriv->x, itemPriv->y);
1630 for (int ii = itemPriv->transforms.count() - 1; ii >= 0; --ii)
1631 itemPriv->transforms.at(ii)->applyTo(&matrix);
1633 if (itemPriv->scale != 1. || itemPriv->rotation != 0.) {
1634 QPointF origin = item->transformOriginPoint();
1635 matrix.translate(origin.x(), origin.y());
1636 if (itemPriv->scale != 1.)
1637 matrix.scale(itemPriv->scale, itemPriv->scale);
1638 if (itemPriv->rotation != 0.)
1639 matrix.rotate(itemPriv->rotation, 0, 0, 1);
1640 matrix.translate(-origin.x(), -origin.y());
1643 itemPriv->itemNode()->setMatrix(matrix);
1646 bool clipEffectivelyChanged = dirty & QQuickItemPrivate::Clip &&
1647 ((item->clip() == false) != (itemPriv->clipNode == 0));
1648 bool effectRefEffectivelyChanged = dirty & QQuickItemPrivate::EffectReference &&
1649 ((itemPriv->effectRefCount == 0) != (itemPriv->rootNode == 0));
1651 if (clipEffectivelyChanged) {
1652 QSGNode *parent = itemPriv->opacityNode ? (QSGNode *) itemPriv->opacityNode : (QSGNode *)itemPriv->itemNode();
1653 QSGNode *child = itemPriv->rootNode ? (QSGNode *)itemPriv->rootNode : (QSGNode *)itemPriv->groupNode;
1656 Q_ASSERT(itemPriv->clipNode == 0);
1657 itemPriv->clipNode = new QQuickDefaultClipNode(item->boundingRect());
1658 itemPriv->clipNode->update();
1661 parent->removeChildNode(child);
1662 parent->appendChildNode(itemPriv->clipNode);
1664 itemPriv->clipNode->appendChildNode(child);
1667 Q_ASSERT(itemPriv->clipNode != 0);
1668 parent->removeChildNode(itemPriv->clipNode);
1670 itemPriv->clipNode->removeChildNode(child);
1671 delete itemPriv->clipNode;
1672 itemPriv->clipNode = 0;
1674 parent->appendChildNode(child);
1678 if (dirty & QQuickItemPrivate::ChildrenUpdateMask)
1679 itemPriv->childContainerNode()->removeAllChildNodes();
1681 if (effectRefEffectivelyChanged) {
1682 QSGNode *parent = itemPriv->clipNode;
1684 parent = itemPriv->opacityNode;
1686 parent = itemPriv->itemNode();
1687 QSGNode *child = itemPriv->groupNode;
1689 if (itemPriv->effectRefCount) {
1690 Q_ASSERT(itemPriv->rootNode == 0);
1691 itemPriv->rootNode = new QSGRootNode;
1694 parent->removeChildNode(child);
1695 parent->appendChildNode(itemPriv->rootNode);
1697 itemPriv->rootNode->appendChildNode(child);
1699 Q_ASSERT(itemPriv->rootNode != 0);
1700 parent->removeChildNode(itemPriv->rootNode);
1702 itemPriv->rootNode->removeChildNode(child);
1703 delete itemPriv->rootNode;
1704 itemPriv->rootNode = 0;
1706 parent->appendChildNode(child);
1710 if (dirty & QQuickItemPrivate::ChildrenUpdateMask) {
1711 QSGNode *groupNode = itemPriv->groupNode;
1713 groupNode->removeAllChildNodes();
1715 QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems();
1718 for (; ii < orderedChildren.count() && orderedChildren.at(ii)->z() < 0; ++ii) {
1719 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
1720 if (!childPrivate->explicitVisible && !childPrivate->effectRefCount)
1722 if (childPrivate->itemNode()->parent())
1723 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
1725 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
1727 itemPriv->beforePaintNode = itemPriv->groupNode ? itemPriv->groupNode->lastChild() : 0;
1729 if (itemPriv->paintNode)
1730 itemPriv->childContainerNode()->appendChildNode(itemPriv->paintNode);
1732 for (; ii < orderedChildren.count(); ++ii) {
1733 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
1734 if (!childPrivate->explicitVisible && !childPrivate->effectRefCount)
1736 if (childPrivate->itemNode()->parent())
1737 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
1739 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
1743 if ((dirty & QQuickItemPrivate::Size) && itemPriv->clipNode) {
1744 itemPriv->clipNode->setRect(item->boundingRect());
1745 itemPriv->clipNode->update();
1748 if (dirty & (QQuickItemPrivate::OpacityValue | QQuickItemPrivate::Visible | QQuickItemPrivate::HideReference)) {
1749 qreal opacity = itemPriv->explicitVisible && itemPriv->hideRefCount == 0
1750 ? itemPriv->opacity : qreal(0);
1752 if (opacity != 1 && !itemPriv->opacityNode) {
1753 itemPriv->opacityNode = new QSGOpacityNode;
1755 QSGNode *parent = itemPriv->itemNode();
1756 QSGNode *child = itemPriv->clipNode;
1758 child = itemPriv->rootNode;
1760 child = itemPriv->groupNode;
1763 parent->removeChildNode(child);
1764 parent->appendChildNode(itemPriv->opacityNode);
1766 itemPriv->opacityNode->appendChildNode(child);
1768 if (itemPriv->opacityNode)
1769 itemPriv->opacityNode->setOpacity(opacity);
1772 if (dirty & QQuickItemPrivate::ContentUpdateMask) {
1774 if (itemPriv->flags & QQuickItem::ItemHasContents) {
1775 updatePaintNodeData.transformNode = itemPriv->itemNode();
1776 itemPriv->paintNode = item->updatePaintNode(itemPriv->paintNode, &updatePaintNodeData);
1778 Q_ASSERT(itemPriv->paintNode == 0 ||
1779 itemPriv->paintNode->parent() == 0 ||
1780 itemPriv->paintNode->parent() == itemPriv->childContainerNode());
1782 if (itemPriv->paintNode && itemPriv->paintNode->parent() == 0) {
1783 if (itemPriv->beforePaintNode)
1784 itemPriv->childContainerNode()->insertChildNodeAfter(itemPriv->paintNode, itemPriv->beforePaintNode);
1786 itemPriv->childContainerNode()->prependChildNode(itemPriv->paintNode);
1788 } else if (itemPriv->paintNode) {
1789 delete itemPriv->paintNode;
1790 itemPriv->paintNode = 0;
1795 // Check consistency.
1796 const QSGNode *nodeChain[] = {
1797 itemPriv->itemNodeInstance,
1798 itemPriv->opacityNode,
1801 itemPriv->groupNode,
1802 itemPriv->paintNode,
1807 while (ip < 5 && nodeChain[ip] == 0)
1812 while (ic < 5 && nodeChain[ic] == 0)
1814 const QSGNode *parent = nodeChain[ip];
1815 const QSGNode *child = nodeChain[ic];
1817 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 0);
1819 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 1);
1820 Q_ASSERT(child->parent() == parent);
1821 bool containsChild = false;
1822 for (QSGNode *n = parent->firstChild(); n; n = n->nextSibling())
1823 containsChild |= (n == child);
1824 Q_ASSERT(containsChild);
1830 #ifdef QML_RUNTIME_TESTING
1831 if (itemPriv->sceneGraphContext()->isFlashModeEnabled()) {
1832 QSGFlashNode *flash = new QSGFlashNode();
1833 flash->setRect(item->boundingRect());
1834 itemPriv->childContainerNode()->appendChildNode(flash);
1845 void QQuickCanvas::maybeUpdate()
1849 if (d->thread && d->thread->isRunning())
1850 d->thread->maybeUpdate();
1854 \fn void QSGEngine::sceneGraphInitialized();
1856 This signal is emitted when the scene graph has been initialized.
1858 This signal will be emitted from the scene graph rendering thread.
1862 Returns the QSGEngine used for this scene.
1864 The engine will only be available once the scene graph has been
1865 initialized. Register for the sceneGraphEngine() signal to get
1866 notification about this.
1869 QSGEngine *QQuickCanvas::sceneGraphEngine() const
1871 Q_D(const QQuickCanvas);
1872 if (d->context && d->context->isReady())
1873 return d->context->engine();
1880 Sets the render target for this canvas to be \a fbo.
1882 The specified fbo must be created in the context of the canvas
1883 or one that shares with it.
1886 This function can only be called from the thread doing
1890 void QQuickCanvas::setRenderTarget(QOpenGLFramebufferObject *fbo)
1893 if (d->context && d->context && QThread::currentThread() != d->context->thread()) {
1894 qWarning("QQuickCanvas::setRenderThread: Cannot set render target from outside the rendering thread");
1898 d->renderTarget = fbo;
1904 Returns the render target for this canvas.
1906 The default is to render to the surface of the canvas, in which
1907 case the render target is 0.
1909 QOpenGLFramebufferObject *QQuickCanvas::renderTarget() const
1911 Q_D(const QQuickCanvas);
1912 return d->renderTarget;
1917 Grabs the contents of the framebuffer and returns it as an image.
1919 This function might not work if the view is not visible.
1921 \warning Calling this function will cause performance problems.
1923 \warning This function can only be called from the GUI thread.
1925 QImage QQuickCanvas::grabFrameBuffer()
1928 return d->thread ? d->thread->grab() : QImage();
1932 Returns an incubation controller that splices incubation between frames
1933 for this canvas. QQuickView automatically installs this controller for you.
1935 The controller is owned by the canvas and will be destroyed when the canvas
1938 QDeclarativeIncubationController *QQuickCanvas::incubationController() const
1940 Q_D(const QQuickCanvas);
1942 if (!d->incubationController)
1943 d->incubationController = new QQuickCanvasIncubationController(const_cast<QQuickCanvasPrivate *>(d));
1944 return d->incubationController;
1948 void QQuickCanvasRenderLoop::createGLContext()
1950 gl = new QOpenGLContext();
1951 gl->setFormat(renderer->requestedFormat());
1955 void QQuickCanvasRenderThread::run()
1958 qDebug("QML Rendering Thread Started");
1964 initializeSceneGraph();
1969 while (!shouldExit) {
1972 bool sizeChanged = false;
1973 isExternalUpdatePending = false;
1975 if (renderedSize != windowSize) {
1977 printf(" RenderThread: window has changed size...\n");
1979 glViewport(0, 0, windowSize.width(), windowSize.height());
1984 printf(" RenderThread: preparing to sync...\n");
1987 if (!isGuiBlocked) {
1988 isGuiBlockPending = true;
1991 printf(" RenderThread: aquired sync lock...\n");
1993 allowMainThreadProcessingFlag = false;
1994 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
1996 printf(" RenderThread: going to sleep...\n");
2000 isGuiBlockPending = false;
2004 printf(" RenderThread: Doing locked sync\n");
2006 #ifdef QQUICK_CANVAS_TIMING
2007 if (qquick_canvas_timing)
2008 threadTimer.start();
2014 // Wake GUI after sync to let it continue animating and event processing.
2015 allowMainThreadProcessingFlag = true;
2019 printf(" RenderThread: sync done\n");
2021 #ifdef QQUICK_CANVAS_TIMING
2022 if (qquick_canvas_timing)
2023 syncTime = threadTimer.elapsed();
2027 printf(" RenderThread: rendering... %d x %d\n", windowSize.width(), windowSize.height());
2030 renderSceneGraph(windowSize);
2031 #ifdef QQUICK_CANVAS_TIMING
2032 if (qquick_canvas_timing)
2033 renderTime = threadTimer.elapsed() - syncTime;
2036 // The content of the target buffer is undefined after swap() so grab needs
2037 // to happen before swap();
2040 printf(" RenderThread: doing a grab...\n");
2042 grabContent = qt_gl_read_framebuffer(windowSize, false, false);
2047 printf(" RenderThread: wait for swap...\n");
2052 printf(" RenderThread: swap complete...\n");
2054 #ifdef QQUICK_CANVAS_TIMING
2055 if (qquick_canvas_timing) {
2056 swapTime = threadTimer.elapsed() - renderTime;
2057 qDebug() << "- Breakdown of frame time: sync:" << syncTime
2058 << "ms render:" << renderTime << "ms swap:" << swapTime
2059 << "ms total:" << swapTime + renderTime << "ms";
2064 isPaintCompleted = true;
2066 renderedSize = windowSize;
2068 // Wake the GUI thread now that rendering is complete, to signal that painting
2069 // is done, resizing is done or grabbing is completed. For grabbing, we're
2070 // signalling this much later than needed (we could have done it before swap)
2071 // but we don't want to lock an extra time.
2074 if (!animationRunning && !isExternalUpdatePending && !shouldExit && !doGrab) {
2076 printf(" RenderThread: nothing to do, going to sleep...\n");
2078 isRenderBlocked = true;
2080 isRenderBlocked = false;
2085 // Process any "deleteLater" objects...
2086 QCoreApplication::processEvents();
2090 printf(" RenderThread: render loop exited... Good Night!\n");
2098 printf(" RenderThread: waking GUI for final sleep..\n");
2104 printf(" RenderThread: All done...\n");
2110 bool QQuickCanvasRenderThread::event(QEvent *e)
2112 Q_ASSERT(QThread::currentThread() == qApp->thread());
2114 if (e->type() == QEvent::User) {
2115 if (!syncAlreadyHappened)
2118 syncAlreadyHappened = false;
2120 if (animationRunning && animationDriver()) {
2122 qDebug("GUI: Advancing animations...\n");
2125 animationDriver()->advance();
2128 qDebug("GUI: Animations advanced...\n");
2135 return QThread::event(e);
2140 void QQuickCanvasRenderThread::exhaustSyncEvent()
2142 if (isGuiBlockPending) {
2144 syncAlreadyHappened = true;
2150 void QQuickCanvasRenderThread::sync(bool guiAlreadyLocked)
2153 printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event");
2155 if (!guiAlreadyLocked)
2158 renderThreadAwakened = false;
2165 if (!guiAlreadyLocked)
2173 Acquires the mutex for the GUI thread. The function uses the isGuiBlocked
2174 variable to keep track of how many recursion levels the gui is locket with.
2175 We only actually acquire the mutex for the first level to avoid deadlocking
2179 void QQuickCanvasRenderThread::lockInGui()
2181 // We must avoid recursive locking in the GUI thread, hence we
2182 // only lock when we are the first one to try to block.
2189 printf("GUI: aquired lock... %d\n", isGuiBlocked);
2195 void QQuickCanvasRenderThread::unlockInGui()
2198 printf("GUI: releasing lock... %d\n", isGuiBlocked);
2208 void QQuickCanvasRenderThread::animationStarted()
2211 printf("GUI: animationStarted()\n");
2216 animationRunning = true;
2218 if (isRenderBlocked)
2226 void QQuickCanvasRenderThread::animationStopped()
2229 printf("GUI: animationStopped()...\n");
2233 animationRunning = false;
2238 void QQuickCanvasRenderThread::paint()
2241 printf("GUI: paint called..\n");
2247 isPaintCompleted = false;
2248 while (isRunning() && !isPaintCompleted) {
2249 if (isRenderBlocked)
2258 void QQuickCanvasRenderThread::resize(const QSize &size)
2261 printf("GUI: Resize Event: %dx%d\n", size.width(), size.height());
2274 while (isRunning() && renderedSize != windowSize) {
2275 if (isRenderBlocked)
2284 void QQuickCanvasRenderThread::startRendering()
2287 printf("GUI: Starting Render Thread\n");
2292 isGuiBlockPending = false;
2298 void QQuickCanvasRenderThread::stopRendering()
2301 printf("GUI: stopping render thread\n");
2308 if (isRenderBlocked) {
2310 printf("GUI: waking up render thread\n");
2315 while (!hasExited) {
2317 printf("GUI: waiting for render thread to have exited..\n");
2325 printf("GUI: waiting for render thread to terminate..\n");
2327 // Actually wait for the thread to terminate. Otherwise we can delete it
2328 // too early and crash.
2332 printf("GUI: thread has terminated and we're all good..\n");
2339 QImage QQuickCanvasRenderThread::grab()
2344 if (QThread::currentThread() != qApp->thread()) {
2345 qWarning("QQuickCanvas::grabFrameBuffer: can only be called from the GUI thread");
2350 printf("GUI: doing a pixelwise grab..\n");
2357 isPaintCompleted = false;
2358 while (isRunning() && !isPaintCompleted) {
2359 if (isRenderBlocked)
2364 QImage grabbed = grabContent;
2365 grabContent = QImage();
2374 void QQuickCanvasRenderThread::maybeUpdate()
2376 Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync,
2377 "QQuickCanvas::update",
2378 "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()");
2381 isExternalUpdatePending = true;
2383 } else if (!renderThreadAwakened) {
2385 printf("GUI: doing update...\n");
2387 renderThreadAwakened = true;
2389 isExternalUpdatePending = true;
2390 if (isRenderBlocked)
2397 #include "moc_qquickcanvas.cpp"