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)
137 virtual void paint() {
138 updatePending = false;
139 if (animationRunning && animationDriver())
140 animationDriver()->advance();
144 glViewport(0, 0, size.width(), size.height());
145 renderSceneGraph(size);
148 if (animationRunning)
152 virtual QImage grab() {
153 return qt_gl_read_framebuffer(size, false, false);
156 virtual void startRendering() {
160 initializeSceneGraph();
167 virtual void stopRendering() { }
169 virtual void maybeUpdate() {
170 if (!updatePending) {
171 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
172 updatePending = true;
176 virtual void animationStarted() {
177 animationRunning = true;
181 virtual void animationStopped() {
182 animationRunning = false;
185 virtual bool isRunning() const { return glContext(); } // Event loop is always running...
186 virtual void resize(const QSize &s) { size = s; }
187 virtual void setWindowSize(const QSize &s) { size = s; }
189 bool event(QEvent *e) {
190 if (e->type() == QEvent::User) {
194 return QObject::event(e);
199 uint updatePending : 1;
200 uint animationRunning : 1;
209 Prior to being added to a valid canvas items can set and clear focus with no
210 effect. Only once items are added to a canvas (by way of having a parent set that
211 already belongs to a canvas) do the focus rules apply. Focus goes back to
212 having no effect if an item is removed from a canvas.
214 When an item is moved into a new focus scope (either being added to a canvas
215 for the first time, or having its parent changed), if the focus scope already has
216 a scope focused item that takes precedence over the item being added. Otherwise,
217 the focus of the added tree is used. In the case of of a tree of items being
218 added to a canvas for the first time, which may have a conflicted focus state (two
219 or more items in one scope having focus set), the same rule is applied item by item -
220 thus the first item that has focus will get it (assuming the scope doesn't already
221 have a scope focused item), and the other items will have their focus cleared.
228 The threaded rendering uses a number of different variables to track potential
229 states used to handle resizing, initial paint, grabbing and driving animations
230 while ALWAYS keeping the GL context in the rendering thread and keeping the
231 overhead of normal one-shot paints and vblank driven animations at a minimum.
233 Resize, initial show and grab suffer slightly in this model as they are locked
234 to the rendering in the rendering thread, but this is a necessary evil for
237 Variables that are used:
239 Private::animationRunning: This is true while the animations are running, and only
240 written to inside locks.
242 RenderThread::isGuiBlocked: This is used to indicate that the GUI thread owns the
243 lock. This variable is an integer to allow for recursive calls to lockInGui()
244 without using a recursive mutex. See isGuiBlockPending.
246 RenderThread::isPaintComplete: This variable is cleared when rendering starts and
247 set once rendering is complete. It is monitored in the paintEvent(),
248 resizeEvent() and grab() functions to force them to wait for rendering to
251 RenderThread::isGuiBlockPending: This variable is set in the render thread just
252 before the sync event is sent to the GUI thread. It is used to avoid deadlocks
253 in the case where render thread waits while waiting for GUI to pick up the sync
254 event and GUI thread gets a resizeEvent, the initial paintEvent or a grab.
255 When this happens, we use the
256 exhaustSyncEvent() function to do the sync right there and mark the coming
257 sync event to be discarded. There can only ever be one sync incoming.
259 RenderThread::isRenderBlock: This variable is true when animations are not
260 running and the render thread has gone to sleep, waiting for more to do.
262 RenderThread::isExternalUpdatePending: This variable is set to false when
263 a new render pass is started and to true in maybeUpdate(). It is an
264 indication to the render thread that another render pass needs to take
265 place, rather than the render thread going to sleep after completing its swap.
267 RenderThread::doGrab: This variable is set by the grab() function and
268 tells the renderer to do a grab after rendering is complete and before
271 RenderThread::shouldExit: This variable is used to determine if the render
272 thread should do a nother pass. It is typically set as a result of show()
273 and unset as a result of hide() or during shutdown()
275 RenderThread::hasExited: Used by the GUI thread to synchronize the shutdown
276 after shouldExit has been set to true.
279 // #define FOCUS_DEBUG
280 // #define MOUSE_DEBUG
281 // #define TOUCH_DEBUG
282 // #define DIRTY_DEBUG
283 // #define THREAD_DEBUG
285 // #define FRAME_TIMING
288 static QTime frameTimer;
289 int sceneGraphRenderTime;
293 QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData()
298 QQuickRootItem::QQuickRootItem()
302 void QQuickCanvas::exposeEvent(QExposeEvent *)
308 void QQuickCanvas::resizeEvent(QResizeEvent *)
311 d->thread->resize(size());
314 void QQuickCanvas::animationStarted()
316 d_func()->thread->animationStarted();
319 void QQuickCanvas::animationStopped()
321 d_func()->thread->animationStopped();
324 void QQuickCanvas::showEvent(QShowEvent *)
327 if (d->vsyncAnimations) {
328 if (!d->animationDriver) {
329 d->animationDriver = d->context->createAnimationDriver(this);
330 connect(d->animationDriver, SIGNAL(started()), this, SLOT(animationStarted()), Qt::DirectConnection);
331 connect(d->animationDriver, SIGNAL(stopped()), this, SLOT(animationStopped()), Qt::DirectConnection);
333 d->animationDriver->install();
336 if (!d->thread->isRunning()) {
337 d->thread->setWindowSize(size());
338 d->thread->startRendering();
342 void QQuickCanvas::hideEvent(QHideEvent *)
345 d->thread->stopRendering();
348 void QQuickCanvas::focusOutEvent(QFocusEvent *)
351 d->rootItem->setFocus(false);
354 void QQuickCanvas::focusInEvent(QFocusEvent *)
357 d->rootItem->setFocus(true);
362 Sets weither this canvas should use vsync driven animations.
364 This option can only be set on one single QQuickCanvas, and that it's
365 vsync signal will then be used to drive all animations in the
368 This feature is primarily useful for single QQuickCanvas, QML-only
371 \warning Enabling vsync on multiple QQuickCanvas instances has
374 void QQuickCanvas::setVSyncAnimations(bool enabled)
378 qWarning("QQuickCanvas::setVSyncAnimations: Cannot be changed when widget is shown");
381 d->vsyncAnimations = enabled;
387 Returns true if this canvas should use vsync driven animations;
388 otherwise returns false.
390 bool QQuickCanvas::vsyncAnimations() const
392 Q_D(const QQuickCanvas);
393 return d->vsyncAnimations;
396 void QQuickCanvasPrivate::initializeSceneGraph()
399 context = QSGContext::createDefaultContext();
401 if (context->isReady())
404 QOpenGLContext *glctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
405 context->initialize(glctx);
408 QObject::connect(context->renderer(), SIGNAL(sceneGraphChanged()), q, SLOT(maybeUpdate()),
409 Qt::DirectConnection);
411 if (!QQuickItemPrivate::get(rootItem)->itemNode()->parent()) {
412 context->rootNode()->appendChildNode(QQuickItemPrivate::get(rootItem)->itemNode());
415 emit q_func()->sceneGraphInitialized();
418 void QQuickCanvasPrivate::polishItems()
420 while (!itemsToPolish.isEmpty()) {
421 QSet<QQuickItem *>::Iterator iter = itemsToPolish.begin();
422 QQuickItem *item = *iter;
423 itemsToPolish.erase(iter);
424 QQuickItemPrivate::get(item)->polishScheduled = false;
425 item->updatePolish();
427 updateFocusItemTransform();
431 void QQuickCanvasPrivate::syncSceneGraph()
437 void QQuickCanvasPrivate::renderSceneGraph(const QSize &size)
439 context->renderer()->setDeviceRect(QRect(QPoint(0, 0), size));
440 context->renderer()->setViewportRect(QRect(QPoint(0, 0), renderTarget ? renderTarget->size() : size));
441 context->renderer()->setProjectionMatrixToDeviceRect();
443 context->renderNextFrame(renderTarget);
446 sceneGraphRenderTime = frameTimer.elapsed();
451 // glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel);
452 readbackTime = frameTimer.elapsed();
457 // ### Do we need this?
458 void QQuickCanvas::sceneGraphChanged()
460 // Q_D(QQuickCanvas);
461 // d->needsRepaint = true;
464 QQuickCanvasPrivate::QQuickCanvasPrivate()
467 , mouseGrabberItem(0)
470 , vsyncAnimations(false)
474 , incubationController(0)
478 QQuickCanvasPrivate::~QQuickCanvasPrivate()
482 void QQuickCanvasPrivate::init(QQuickCanvas *c)
484 QUnifiedTimer* ut = QUnifiedTimer::instance(true);
485 if (qmlFixedAnimationStep())
486 ut->setConsistentTiming(true);
492 rootItem = new QQuickRootItem;
493 QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(rootItem);
494 rootItemPrivate->canvas = q;
495 rootItemPrivate->flags |= QQuickItem::ItemIsFocusScope;
497 // In the absence of a focus in event on some platforms assume the window will
498 // be activated immediately and set focus on the rootItem
499 // ### Remove when QTBUG-22415 is resolved.
500 //It is important that this call happens after the rootItem has a canvas..
501 rootItem->setFocus(true);
503 bool threaded = !qmlNoThreadedRenderer();
505 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL)) {
506 qWarning("QQuickCanvas: platform does not support threaded rendering!");
511 thread = new QQuickCanvasRenderThread();
513 thread = new QQuickCanvasPlainRenderLoop();
515 thread->renderer = q;
518 context = QSGContext::createDefaultContext();
519 thread->moveContextToThread(context);
521 q->setSurfaceType(QWindow::OpenGLSurface);
522 q->setFormat(context->defaultSurfaceFormat());
525 void QQuickCanvasPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform)
527 for (int i=0; i<touchPoints.count(); i++) {
528 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
529 touchPoint.setRect(transform.mapRect(touchPoint.sceneRect()));
530 touchPoint.setStartPos(transform.map(touchPoint.startScenePos()));
531 touchPoint.setLastPos(transform.map(touchPoint.lastScenePos()));
537 Translates the data in \a touchEvent to this canvas. This method leaves the item local positions in
538 \a touchEvent untouched (these are filled in later).
540 void QQuickCanvasPrivate::translateTouchEvent(QTouchEvent *touchEvent)
542 // Q_Q(QQuickCanvas);
544 // touchEvent->setWidget(q); // ### refactor...
546 QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
547 for (int i = 0; i < touchPoints.count(); ++i) {
548 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
550 touchPoint.setScreenRect(touchPoint.sceneRect());
551 touchPoint.setStartScreenPos(touchPoint.startScenePos());
552 touchPoint.setLastScreenPos(touchPoint.lastScenePos());
554 touchPoint.setSceneRect(touchPoint.rect());
555 touchPoint.setStartScenePos(touchPoint.startPos());
556 touchPoint.setLastScenePos(touchPoint.lastPos());
558 if (touchPoint.isPrimary())
559 lastMousePosition = touchPoint.pos().toPoint();
561 touchEvent->setTouchPoints(touchPoints);
564 void QQuickCanvasPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
569 Q_ASSERT(scope || item == rootItem);
572 qWarning() << "QQuickCanvasPrivate::setFocusInScope():";
573 qWarning() << " scope:" << (QObject *)scope;
575 qWarning() << " scopeSubFocusItem:" << (QObject *)QQuickItemPrivate::get(scope)->subFocusItem;
576 qWarning() << " item:" << (QObject *)item;
577 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
580 QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0;
581 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
583 QQuickItem *oldActiveFocusItem = 0;
584 QQuickItem *newActiveFocusItem = 0;
586 QVarLengthArray<QQuickItem *, 20> changed;
588 // Does this change the active focus?
589 if (item == rootItem || scopePrivate->activeFocus) {
590 oldActiveFocusItem = activeFocusItem;
591 newActiveFocusItem = item;
592 while (newActiveFocusItem->isFocusScope() && newActiveFocusItem->scopedFocusItem())
593 newActiveFocusItem = newActiveFocusItem->scopedFocusItem();
595 if (oldActiveFocusItem) {
597 qApp->inputPanel()->commit();
601 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
602 q->sendEvent(oldActiveFocusItem, &event);
604 QQuickItem *afi = oldActiveFocusItem;
605 while (afi != scope) {
606 if (QQuickItemPrivate::get(afi)->activeFocus) {
607 QQuickItemPrivate::get(afi)->activeFocus = false;
610 afi = afi->parentItem();
615 if (item != rootItem) {
616 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
617 // Correct focus chain in scope
618 if (oldSubFocusItem) {
619 QQuickItem *sfi = scopePrivate->subFocusItem->parentItem();
620 while (sfi != scope) {
621 QQuickItemPrivate::get(sfi)->subFocusItem = 0;
622 sfi = sfi->parentItem();
626 scopePrivate->subFocusItem = item;
627 QQuickItem *sfi = scopePrivate->subFocusItem->parentItem();
628 while (sfi != scope) {
629 QQuickItemPrivate::get(sfi)->subFocusItem = item;
630 sfi = sfi->parentItem();
634 if (oldSubFocusItem) {
635 QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
636 changed << oldSubFocusItem;
640 if (!(options & DontChangeFocusProperty)) {
641 // if (item != rootItem || QGuiApplication::focusWindow() == q) { // QTBUG-22415
642 itemPrivate->focus = true;
647 if (newActiveFocusItem && rootItem->hasFocus()) {
648 activeFocusItem = newActiveFocusItem;
650 QQuickItemPrivate::get(newActiveFocusItem)->activeFocus = true;
651 changed << newActiveFocusItem;
653 QQuickItem *afi = newActiveFocusItem->parentItem();
654 while (afi && afi != scope) {
655 if (afi->isFocusScope()) {
656 QQuickItemPrivate::get(afi)->activeFocus = true;
659 afi = afi->parentItem();
662 updateInputMethodData();
664 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
665 q->sendEvent(newActiveFocusItem, &event);
667 updateInputMethodData();
670 if (!changed.isEmpty())
671 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
674 void QQuickCanvasPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
680 Q_ASSERT(scope || item == rootItem);
683 qWarning() << "QQuickCanvasPrivate::clearFocusInScope():";
684 qWarning() << " scope:" << (QObject *)scope;
685 qWarning() << " item:" << (QObject *)item;
686 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
689 QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0;
691 QQuickItem *oldActiveFocusItem = 0;
692 QQuickItem *newActiveFocusItem = 0;
694 QVarLengthArray<QQuickItem *, 20> changed;
696 Q_ASSERT(item == rootItem || item == scopePrivate->subFocusItem);
698 // Does this change the active focus?
699 if (item == rootItem || scopePrivate->activeFocus) {
700 oldActiveFocusItem = activeFocusItem;
701 newActiveFocusItem = scope;
703 Q_ASSERT(oldActiveFocusItem);
706 qApp->inputPanel()->commit();
710 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
711 q->sendEvent(oldActiveFocusItem, &event);
713 QQuickItem *afi = oldActiveFocusItem;
714 while (afi != scope) {
715 if (QQuickItemPrivate::get(afi)->activeFocus) {
716 QQuickItemPrivate::get(afi)->activeFocus = false;
719 afi = afi->parentItem();
723 if (item != rootItem) {
724 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
725 // Correct focus chain in scope
726 if (oldSubFocusItem) {
727 QQuickItem *sfi = scopePrivate->subFocusItem->parentItem();
728 while (sfi != scope) {
729 QQuickItemPrivate::get(sfi)->subFocusItem = 0;
730 sfi = sfi->parentItem();
733 scopePrivate->subFocusItem = 0;
735 if (oldSubFocusItem && !(options & DontChangeFocusProperty)) {
736 QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
737 changed << oldSubFocusItem;
739 } else if (!(options & DontChangeFocusProperty)) {
740 QQuickItemPrivate::get(item)->focus = false;
744 if (newActiveFocusItem) {
745 Q_ASSERT(newActiveFocusItem == scope);
746 activeFocusItem = scope;
748 updateInputMethodData();
750 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
751 q->sendEvent(newActiveFocusItem, &event);
753 updateInputMethodData();
756 if (!changed.isEmpty())
757 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
760 void QQuickCanvasPrivate::notifyFocusChangesRecur(QQuickItem **items, int remaining)
762 QDeclarativeGuard<QQuickItem> item(*items);
765 notifyFocusChangesRecur(items + 1, remaining - 1);
768 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
770 if (itemPrivate->notifiedFocus != itemPrivate->focus) {
771 itemPrivate->notifiedFocus = itemPrivate->focus;
772 emit item->focusChanged(itemPrivate->focus);
775 if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) {
776 itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus;
777 itemPrivate->itemChange(QQuickItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus);
778 emit item->activeFocusChanged(itemPrivate->activeFocus);
783 void QQuickCanvasPrivate::updateInputMethodData()
785 QQuickItem *inputItem = 0;
786 if (activeFocusItem && activeFocusItem->flags() & QQuickItem::ItemAcceptsInputMethod)
787 inputItem = activeFocusItem;
788 qApp->inputPanel()->setInputItem(inputItem);
792 Queries the Input Method.
794 QVariant QQuickCanvas::inputMethodQuery(Qt::InputMethodQuery query) const
796 Q_D(const QQuickCanvas);
797 if (!d->activeFocusItem || !(QQuickItemPrivate::get(d->activeFocusItem)->flags & QQuickItem::ItemAcceptsInputMethod))
799 QVariant value = d->activeFocusItem->inputMethodQuery(query);
802 QVariant::Type type = value.type();
803 if (type == QVariant::RectF || type == QVariant::Rect) {
804 const QTransform transform = QQuickItemPrivate::get(d->activeFocusItem)->itemToCanvasTransform();
805 value = transform.mapRect(value.toRectF());
806 } else if (type == QVariant::PointF || type == QVariant::Point) {
807 const QTransform transform = QQuickItemPrivate::get(d->activeFocusItem)->itemToCanvasTransform();
808 value = transform.map(value.toPointF());
813 void QQuickCanvasPrivate::dirtyItem(QQuickItem *)
819 void QQuickCanvasPrivate::cleanup(QSGNode *n)
823 Q_ASSERT(!cleanupNodeList.contains(n));
824 cleanupNodeList.append(n);
832 \brief The QQuickCanvas class provides the canvas for displaying a graphical QML scene
834 QQuickCanvas provides the graphical scene management needed to interact with and display
835 a scene of QQuickItems.
837 A QQuickCanvas always has a single invisible root item. To add items to this canvas,
838 reparent the items to the root item or to an existing item in the scene.
840 For easily displaying a scene from a QML file, see \l{QQuickView}.
842 QQuickCanvas::QQuickCanvas(QWindow *parent)
843 : QWindow(*(new QQuickCanvasPrivate), parent)
849 QQuickCanvas::QQuickCanvas(QQuickCanvasPrivate &dd, QWindow *parent)
850 : QWindow(dd, parent)
856 QQuickCanvas::~QQuickCanvas()
860 /* The threaded renderer will clean up the nodes which will fire
861 sceneGraphChanged events through back to the canvas. This signal
862 is connected to maybeUpdate which should only be called from GUI or during
863 updatePaintNode(), so disconnect them before starting the shutdown
865 disconnect(d->context->renderer(), SIGNAL(sceneGraphChanged()), this, SLOT(maybeUpdate()));
866 if (d->thread->isRunning())
867 d->thread->stopRendering();
869 // ### should we change ~QQuickItem to handle this better?
870 // manually cleanup for the root item (item destructor only handles these when an item is parented)
871 QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(d->rootItem);
872 rootItemPrivate->removeFromDirtyList();
874 delete d->incubationController; d->incubationController = 0;
876 delete d->rootItem; d->rootItem = 0;
879 delete d->thread; d->thread = 0;
883 Returns the invisible root item of the scene.
885 A QQuickCanvas always has a single invisible root item. To add items to this canvas,
886 reparent the items to the root item or to an existing item in the scene.
888 QQuickItem *QQuickCanvas::rootItem() const
890 Q_D(const QQuickCanvas);
896 Returns the item which currently has active focus.
898 QQuickItem *QQuickCanvas::activeFocusItem() const
900 Q_D(const QQuickCanvas);
902 return d->activeFocusItem;
906 Returns the item which currently has the mouse grab.
908 QQuickItem *QQuickCanvas::mouseGrabberItem() const
910 Q_D(const QQuickCanvas);
912 return d->mouseGrabberItem;
916 bool QQuickCanvasPrivate::clearHover()
918 if (hoverItems.isEmpty())
921 QPointF pos = QCursor::pos(); // ### refactor: q->mapFromGlobal(QCursor::pos());
923 bool accepted = false;
924 foreach (QQuickItem* item, hoverItems)
925 accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), true) || accepted;
931 bool QQuickCanvas::event(QEvent *e)
937 case QEvent::TouchBegin:
938 case QEvent::TouchUpdate:
939 case QEvent::TouchEnd:
941 QTouchEvent *touch = static_cast<QTouchEvent *>(e);
942 d->translateTouchEvent(touch);
943 d->deliverTouchEvent(touch);
944 if (!touch->isAccepted())
950 d->lastMousePosition = QPoint();
952 case QEvent::DragEnter:
953 case QEvent::DragLeave:
954 case QEvent::DragMove:
956 d->deliverDragEvent(&d->dragGrabber, e);
958 case QEvent::WindowDeactivate:
959 rootItem()->windowDeactivateEvent();
965 return QWindow::event(e);
968 void QQuickCanvas::keyPressEvent(QKeyEvent *e)
972 if (d->activeFocusItem)
973 sendEvent(d->activeFocusItem, e);
976 void QQuickCanvas::keyReleaseEvent(QKeyEvent *e)
980 if (d->activeFocusItem)
981 sendEvent(d->activeFocusItem, e);
984 void QQuickCanvas::inputMethodEvent(QInputMethodEvent *e)
988 if (d->activeFocusItem)
989 sendEvent(d->activeFocusItem, e);
992 bool QQuickCanvasPrivate::deliverInitialMousePressEvent(QQuickItem *item, QMouseEvent *event)
996 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
997 if (itemPrivate->opacity == 0.0)
1000 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1001 QPointF p = item->mapFromScene(event->windowPos());
1002 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
1006 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1007 for (int ii = children.count() - 1; ii >= 0; --ii) {
1008 QQuickItem *child = children.at(ii);
1009 if (!child->isVisible() || !child->isEnabled())
1011 if (deliverInitialMousePressEvent(child, event))
1015 if (itemPrivate->acceptedMouseButtons & event->button()) {
1016 QPointF p = item->mapFromScene(event->windowPos());
1017 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1018 QMouseEvent me(event->type(), p, event->windowPos(), event->screenPos(),
1019 event->button(), event->buttons(), event->modifiers());
1021 mouseGrabberItem = item;
1022 q->sendEvent(item, &me);
1023 event->setAccepted(me.isAccepted());
1024 if (me.isAccepted())
1026 mouseGrabberItem->ungrabMouse();
1027 mouseGrabberItem = 0;
1034 bool QQuickCanvasPrivate::deliverMouseEvent(QMouseEvent *event)
1038 lastMousePosition = event->windowPos();
1040 if (!mouseGrabberItem &&
1041 event->type() == QEvent::MouseButtonPress &&
1042 (event->button() & event->buttons()) == event->buttons()) {
1043 return deliverInitialMousePressEvent(rootItem, event);
1046 if (mouseGrabberItem) {
1047 QQuickItemPrivate *mgPrivate = QQuickItemPrivate::get(mouseGrabberItem);
1048 const QTransform &transform = mgPrivate->canvasToItemTransform();
1049 QMouseEvent me(event->type(), transform.map(event->windowPos()), event->windowPos(), event->screenPos(),
1050 event->button(), event->buttons(), event->modifiers());
1052 q->sendEvent(mouseGrabberItem, &me);
1053 event->setAccepted(me.isAccepted());
1054 if (me.isAccepted())
1061 void QQuickCanvas::mousePressEvent(QMouseEvent *event)
1066 qWarning() << "QQuickCanvas::mousePressEvent()" << event->pos() << event->button() << event->buttons();
1069 d->deliverMouseEvent(event);
1072 void QQuickCanvas::mouseReleaseEvent(QMouseEvent *event)
1077 qWarning() << "QQuickCanvas::mouseReleaseEvent()" << event->pos() << event->button() << event->buttons();
1080 if (!d->mouseGrabberItem) {
1081 QWindow::mouseReleaseEvent(event);
1085 d->deliverMouseEvent(event);
1086 d->mouseGrabberItem = 0;
1089 void QQuickCanvas::mouseDoubleClickEvent(QMouseEvent *event)
1094 qWarning() << "QQuickCanvas::mouseDoubleClickEvent()" << event->pos() << event->button() << event->buttons();
1097 if (!d->mouseGrabberItem && (event->button() & event->buttons()) == event->buttons()) {
1098 if (d->deliverInitialMousePressEvent(d->rootItem, event))
1105 d->deliverMouseEvent(event);
1108 bool QQuickCanvasPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
1109 const QPointF &scenePos, const QPointF &lastScenePos,
1110 Qt::KeyboardModifiers modifiers, bool accepted)
1113 const QTransform transform = QQuickItemPrivate::get(item)->canvasToItemTransform();
1115 //create copy of event
1116 QHoverEvent hoverEvent(type, transform.map(scenePos), transform.map(lastScenePos), modifiers);
1117 hoverEvent.setAccepted(accepted);
1119 q->sendEvent(item, &hoverEvent);
1121 return hoverEvent.isAccepted();
1124 void QQuickCanvas::mouseMoveEvent(QMouseEvent *event)
1129 qWarning() << "QQuickCanvas::mouseMoveEvent()" << event->pos() << event->button() << event->buttons();
1132 if (!d->mouseGrabberItem) {
1133 if (d->lastMousePosition.isNull())
1134 d->lastMousePosition = event->windowPos();
1135 QPointF last = d->lastMousePosition;
1136 d->lastMousePosition = event->windowPos();
1138 bool accepted = event->isAccepted();
1139 bool delivered = d->deliverHoverEvent(d->rootItem, event->windowPos(), last, event->modifiers(), accepted);
1141 //take care of any exits
1142 accepted = d->clearHover();
1144 event->setAccepted(accepted);
1148 d->deliverMouseEvent(event);
1151 bool QQuickCanvasPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
1152 Qt::KeyboardModifiers modifiers, bool &accepted)
1154 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1155 if (itemPrivate->opacity == 0.0)
1158 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1159 QPointF p = item->mapFromScene(scenePos);
1160 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
1164 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1165 for (int ii = children.count() - 1; ii >= 0; --ii) {
1166 QQuickItem *child = children.at(ii);
1167 if (!child->isVisible() || !child->isEnabled())
1169 if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, accepted))
1173 if (itemPrivate->hoverEnabled) {
1174 QPointF p = item->mapFromScene(scenePos);
1175 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1176 if (!hoverItems.isEmpty() && hoverItems[0] == item) {
1178 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1180 QList<QQuickItem *> itemsToHover;
1181 QQuickItem* parent = item;
1182 itemsToHover << item;
1183 while ((parent = parent->parentItem()))
1184 itemsToHover << parent;
1186 // Leaving from previous hovered items until we reach the item or one of its ancestors.
1187 while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems[0])) {
1188 sendHoverEvent(QEvent::HoverLeave, hoverItems[0], scenePos, lastScenePos, modifiers, accepted);
1189 hoverItems.removeFirst();
1192 if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item
1193 // ### Shouldn't we send moves for the parent items as well?
1194 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1196 // Enter items that are not entered yet.
1198 if (!hoverItems.isEmpty())
1199 startIdx = itemsToHover.indexOf(hoverItems[0]) - 1;
1201 startIdx = itemsToHover.count() - 1;
1203 for (int i = startIdx; i >= 0; i--) {
1204 QQuickItem *itemToHover = itemsToHover[i];
1205 if (QQuickItemPrivate::get(itemToHover)->hoverEnabled) {
1206 hoverItems.prepend(itemToHover);
1207 sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, accepted);
1219 bool QQuickCanvasPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event)
1222 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1223 if (itemPrivate->opacity == 0.0)
1226 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1227 QPointF p = item->mapFromScene(event->posF());
1228 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
1232 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1233 for (int ii = children.count() - 1; ii >= 0; --ii) {
1234 QQuickItem *child = children.at(ii);
1235 if (!child->isVisible() || !child->isEnabled())
1237 if (deliverWheelEvent(child, event))
1241 QPointF p = item->mapFromScene(event->posF());
1242 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1243 QWheelEvent wheel(p, event->delta(), event->buttons(), event->modifiers(), event->orientation());
1245 q->sendEvent(item, &wheel);
1246 if (wheel.isAccepted()) {
1255 #ifndef QT_NO_WHEELEVENT
1256 void QQuickCanvas::wheelEvent(QWheelEvent *event)
1260 qWarning() << "QQuickCanvas::wheelEvent()" << event->pos() << event->delta() << event->orientation();
1263 d->deliverWheelEvent(d->rootItem, event);
1265 #endif // QT_NO_WHEELEVENT
1267 bool QQuickCanvasPrivate::deliverTouchEvent(QTouchEvent *event)
1270 if (event->type() == QEvent::TouchBegin)
1271 qWarning("touchBeginEvent");
1272 else if (event->type() == QEvent::TouchUpdate)
1273 qWarning("touchUpdateEvent");
1274 else if (event->type() == QEvent::TouchEnd)
1275 qWarning("touchEndEvent");
1278 QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > updatedPoints;
1280 if (event->type() == QTouchEvent::TouchBegin) { // all points are new touch points
1281 QSet<int> acceptedNewPoints;
1282 deliverTouchPoints(rootItem, event, event->touchPoints(), &acceptedNewPoints, &updatedPoints);
1283 if (acceptedNewPoints.count() > 0)
1285 return event->isAccepted();
1288 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
1289 QList<QTouchEvent::TouchPoint> newPoints;
1290 QQuickItem *item = 0;
1291 for (int i=0; i<touchPoints.count(); i++) {
1292 const QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
1293 switch (touchPoint.state()) {
1294 case Qt::TouchPointPressed:
1295 newPoints << touchPoint;
1297 case Qt::TouchPointMoved:
1298 case Qt::TouchPointStationary:
1299 case Qt::TouchPointReleased:
1300 if (itemForTouchPointId.contains(touchPoint.id())) {
1301 item = itemForTouchPointId[touchPoint.id()];
1303 updatedPoints[item].append(touchPoint);
1311 if (newPoints.count() > 0 || updatedPoints.count() > 0) {
1312 QSet<int> acceptedNewPoints;
1313 int prevCount = updatedPoints.count();
1314 deliverTouchPoints(rootItem, event, newPoints, &acceptedNewPoints, &updatedPoints);
1315 if (acceptedNewPoints.count() > 0 || updatedPoints.count() != prevCount)
1319 if (event->touchPointStates() & Qt::TouchPointReleased) {
1320 for (int i=0; i<touchPoints.count(); i++) {
1321 if (touchPoints[i].state() == Qt::TouchPointReleased)
1322 itemForTouchPointId.remove(touchPoints[i].id());
1326 return event->isAccepted();
1329 bool QQuickCanvasPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints, QSet<int> *acceptedNewPoints, QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *updatedPoints)
1332 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1334 if (itemPrivate->opacity == 0.0)
1337 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1338 QRectF bounds(0, 0, item->width(), item->height());
1339 for (int i=0; i<newPoints.count(); i++) {
1340 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1341 if (!bounds.contains(p))
1346 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1347 for (int ii = children.count() - 1; ii >= 0; --ii) {
1348 QQuickItem *child = children.at(ii);
1349 if (!child->isEnabled())
1351 if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints))
1355 QList<QTouchEvent::TouchPoint> matchingPoints;
1356 if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) {
1357 QRectF bounds(0, 0, item->width(), item->height());
1358 for (int i=0; i<newPoints.count(); i++) {
1359 if (acceptedNewPoints->contains(newPoints[i].id()))
1361 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1362 if (bounds.contains(p))
1363 matchingPoints << newPoints[i];
1367 if (matchingPoints.count() > 0 || (*updatedPoints)[item].count() > 0) {
1368 QList<QTouchEvent::TouchPoint> &eventPoints = (*updatedPoints)[item];
1369 eventPoints.append(matchingPoints);
1370 transformTouchPoints(eventPoints, itemPrivate->canvasToItemTransform());
1372 Qt::TouchPointStates eventStates;
1373 for (int i=0; i<eventPoints.count(); i++)
1374 eventStates |= eventPoints[i].state();
1375 // if all points have the same state, set the event type accordingly
1376 QEvent::Type eventType;
1377 switch (eventStates) {
1378 case Qt::TouchPointPressed:
1379 eventType = QEvent::TouchBegin;
1381 case Qt::TouchPointReleased:
1382 eventType = QEvent::TouchEnd;
1385 eventType = QEvent::TouchUpdate;
1389 if (eventStates != Qt::TouchPointStationary) {
1390 QTouchEvent touchEvent(eventType);
1391 // touchEvent.setWidget(q); // ### refactor: what is the consequence of not setting the widget?
1392 touchEvent.setDeviceType(event->deviceType());
1393 touchEvent.setModifiers(event->modifiers());
1394 touchEvent.setTouchPointStates(eventStates);
1395 touchEvent.setTouchPoints(eventPoints);
1396 touchEvent.setTimestamp(event->timestamp());
1398 touchEvent.accept();
1399 q->sendEvent(item, &touchEvent);
1401 if (touchEvent.isAccepted()) {
1402 for (int i=0; i<matchingPoints.count(); i++) {
1403 itemForTouchPointId[matchingPoints[i].id()] = item;
1404 acceptedNewPoints->insert(matchingPoints[i].id());
1410 updatedPoints->remove(item);
1411 if (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty())
1417 void QQuickCanvasPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event)
1420 grabber->resetTarget();
1421 QQuickDragGrabber::iterator grabItem = grabber->begin();
1422 if (grabItem != grabber->end()) {
1423 Q_ASSERT(event->type() != QEvent::DragEnter);
1424 if (event->type() == QEvent::Drop) {
1425 QDropEvent *e = static_cast<QDropEvent *>(event);
1426 for (e->setAccepted(false); !e->isAccepted() && grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1427 QPointF p = (**grabItem)->mapFromScene(e->pos());
1428 QDropEvent translatedEvent(
1430 e->possibleActions(),
1433 e->keyboardModifiers());
1434 QQuickDropEventEx::copyActions(&translatedEvent, *e);
1435 q->sendEvent(**grabItem, &translatedEvent);
1436 e->setAccepted(translatedEvent.isAccepted());
1437 e->setDropAction(translatedEvent.dropAction());
1438 grabber->setTarget(**grabItem);
1441 if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave.
1442 QDragLeaveEvent leaveEvent;
1443 for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
1444 q->sendEvent(**grabItem, &leaveEvent);
1446 } else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1447 QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event);
1448 if (deliverDragEvent(grabber, **grabItem, moveEvent)) {
1449 moveEvent->setAccepted(true);
1450 for (++grabItem; grabItem != grabber->end();) {
1451 QPointF p = (**grabItem)->mapFromScene(moveEvent->pos());
1452 if (QRectF(0, 0, (**grabItem)->width(), (**grabItem)->height()).contains(p)) {
1453 QDragMoveEvent translatedEvent(
1455 moveEvent->possibleActions(),
1456 moveEvent->mimeData(),
1457 moveEvent->mouseButtons(),
1458 moveEvent->keyboardModifiers());
1459 QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent);
1460 q->sendEvent(**grabItem, &translatedEvent);
1463 QDragLeaveEvent leaveEvent;
1464 q->sendEvent(**grabItem, &leaveEvent);
1465 grabItem = grabber->release(grabItem);
1470 QDragLeaveEvent leaveEvent;
1471 q->sendEvent(**grabItem, &leaveEvent);
1475 if (event->type() == QEvent::DragEnter || event->type() == QEvent::DragMove) {
1476 QDragMoveEvent *e = static_cast<QDragMoveEvent *>(event);
1477 QDragEnterEvent enterEvent(
1479 e->possibleActions(),
1482 e->keyboardModifiers());
1483 QQuickDropEventEx::copyActions(&enterEvent, *e);
1484 event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent));
1488 bool QQuickCanvasPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event)
1491 bool accepted = false;
1492 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1493 if (itemPrivate->opacity == 0.0 || !item->isVisible() || !item->isEnabled())
1496 QPointF p = item->mapFromScene(event->pos());
1497 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1498 if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) {
1499 QDragMoveEvent translatedEvent(
1501 event->possibleActions(),
1503 event->mouseButtons(),
1504 event->keyboardModifiers(),
1506 QQuickDropEventEx::copyActions(&translatedEvent, *event);
1507 q->sendEvent(item, &translatedEvent);
1508 if (event->type() == QEvent::DragEnter) {
1509 if (translatedEvent.isAccepted()) {
1510 grabber->grab(item);
1517 } else if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1521 QDragEnterEvent enterEvent(
1523 event->possibleActions(),
1525 event->mouseButtons(),
1526 event->keyboardModifiers());
1527 QQuickDropEventEx::copyActions(&enterEvent, *event);
1528 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1529 for (int ii = children.count() - 1; ii >= 0; --ii) {
1530 if (deliverDragEvent(grabber, children.at(ii), &enterEvent))
1537 bool QQuickCanvasPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QEvent *event)
1542 QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target);
1543 if (targetPrivate->filtersChildMouseEvents)
1544 if (target->childMouseEventFilter(item, event))
1547 if (sendFilteredMouseEvent(target->parentItem(), item, event))
1554 Propagates an event to a QQuickItem on the canvas
1556 bool QQuickCanvas::sendEvent(QQuickItem *item, QEvent *e)
1561 qWarning("QQuickCanvas::sendEvent: Cannot send event to a null item");
1567 switch (e->type()) {
1568 case QEvent::KeyPress:
1569 case QEvent::KeyRelease:
1571 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1572 while (!e->isAccepted() && (item = item->parentItem())) {
1574 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1577 case QEvent::InputMethod:
1579 QQuickItemPrivate::get(item)->deliverInputMethodEvent(static_cast<QInputMethodEvent *>(e));
1580 while (!e->isAccepted() && (item = item->parentItem())) {
1582 QQuickItemPrivate::get(item)->deliverInputMethodEvent(static_cast<QInputMethodEvent *>(e));
1585 case QEvent::FocusIn:
1586 case QEvent::FocusOut:
1587 QQuickItemPrivate::get(item)->deliverFocusEvent(static_cast<QFocusEvent *>(e));
1589 case QEvent::MouseButtonPress:
1590 case QEvent::MouseButtonRelease:
1591 case QEvent::MouseButtonDblClick:
1592 case QEvent::MouseMove:
1593 // XXX todo - should sendEvent be doing this? how does it relate to forwarded events?
1594 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
1596 QQuickItemPrivate::get(item)->deliverMouseEvent(static_cast<QMouseEvent *>(e));
1600 QQuickItemPrivate::get(item)->deliverWheelEvent(static_cast<QWheelEvent *>(e));
1602 case QEvent::HoverEnter:
1603 case QEvent::HoverLeave:
1604 case QEvent::HoverMove:
1605 QQuickItemPrivate::get(item)->deliverHoverEvent(static_cast<QHoverEvent *>(e));
1607 case QEvent::TouchBegin:
1608 case QEvent::TouchUpdate:
1609 case QEvent::TouchEnd:
1610 // XXX todo - should sendEvent be doing this? how does it relate to forwarded events?
1611 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
1613 QQuickItemPrivate::get(item)->deliverTouchEvent(static_cast<QTouchEvent *>(e));
1616 case QEvent::DragEnter:
1617 case QEvent::DragMove:
1618 case QEvent::DragLeave:
1620 QQuickItemPrivate::get(item)->deliverDragEvent(e);
1629 void QQuickCanvasPrivate::cleanupNodes()
1631 for (int ii = 0; ii < cleanupNodeList.count(); ++ii)
1632 delete cleanupNodeList.at(ii);
1633 cleanupNodeList.clear();
1636 void QQuickCanvasPrivate::cleanupNodesOnShutdown(QQuickItem *item)
1638 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
1639 if (p->itemNodeInstance) {
1640 delete p->itemNodeInstance;
1641 p->itemNodeInstance = 0;
1648 for (int ii = 0; ii < p->childItems.count(); ++ii)
1649 cleanupNodesOnShutdown(p->childItems.at(ii));
1652 // This must be called from the render thread, with the main thread frozen
1653 void QQuickCanvasPrivate::cleanupNodesOnShutdown()
1657 cleanupNodesOnShutdown(rootItem);
1660 void QQuickCanvasPrivate::updateDirtyNodes()
1663 qWarning() << "QQuickCanvasPrivate::updateDirtyNodes():";
1668 QQuickItem *updateList = dirtyItemList;
1670 if (updateList) QQuickItemPrivate::get(updateList)->prevDirtyItem = &updateList;
1672 while (updateList) {
1673 QQuickItem *item = updateList;
1674 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
1675 itemPriv->removeFromDirtyList();
1678 qWarning() << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString());
1680 updateDirtyNode(item);
1684 void QQuickCanvasPrivate::updateDirtyNode(QQuickItem *item)
1686 #ifdef QML_RUNTIME_TESTING
1687 bool didFlash = false;
1690 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
1691 quint32 dirty = itemPriv->dirtyAttributes;
1692 itemPriv->dirtyAttributes = 0;
1694 if ((dirty & QQuickItemPrivate::TransformUpdateMask) ||
1695 (dirty & QQuickItemPrivate::Size && itemPriv->origin != QQuickItem::TopLeft &&
1696 (itemPriv->scale != 1. || itemPriv->rotation != 0.))) {
1700 if (itemPriv->x != 0. || itemPriv->y != 0.)
1701 matrix.translate(itemPriv->x, itemPriv->y);
1703 for (int ii = itemPriv->transforms.count() - 1; ii >= 0; --ii)
1704 itemPriv->transforms.at(ii)->applyTo(&matrix);
1706 if (itemPriv->scale != 1. || itemPriv->rotation != 0.) {
1707 QPointF origin = item->transformOriginPoint();
1708 matrix.translate(origin.x(), origin.y());
1709 if (itemPriv->scale != 1.)
1710 matrix.scale(itemPriv->scale, itemPriv->scale);
1711 if (itemPriv->rotation != 0.)
1712 matrix.rotate(itemPriv->rotation, 0, 0, 1);
1713 matrix.translate(-origin.x(), -origin.y());
1716 itemPriv->itemNode()->setMatrix(matrix);
1719 bool clipEffectivelyChanged = dirty & QQuickItemPrivate::Clip &&
1720 ((item->clip() == false) != (itemPriv->clipNode == 0));
1721 bool effectRefEffectivelyChanged = dirty & QQuickItemPrivate::EffectReference &&
1722 ((itemPriv->effectRefCount == 0) != (itemPriv->rootNode == 0));
1724 if (clipEffectivelyChanged) {
1725 QSGNode *parent = itemPriv->opacityNode ? (QSGNode *) itemPriv->opacityNode : (QSGNode *)itemPriv->itemNode();
1726 QSGNode *child = itemPriv->rootNode ? (QSGNode *)itemPriv->rootNode : (QSGNode *)itemPriv->groupNode;
1729 Q_ASSERT(itemPriv->clipNode == 0);
1730 itemPriv->clipNode = new QQuickDefaultClipNode(item->boundingRect());
1731 itemPriv->clipNode->update();
1734 parent->removeChildNode(child);
1735 parent->appendChildNode(itemPriv->clipNode);
1737 itemPriv->clipNode->appendChildNode(child);
1740 Q_ASSERT(itemPriv->clipNode != 0);
1741 parent->removeChildNode(itemPriv->clipNode);
1743 itemPriv->clipNode->removeChildNode(child);
1744 delete itemPriv->clipNode;
1745 itemPriv->clipNode = 0;
1747 parent->appendChildNode(child);
1751 if (dirty & QQuickItemPrivate::ChildrenUpdateMask)
1752 itemPriv->childContainerNode()->removeAllChildNodes();
1754 if (effectRefEffectivelyChanged) {
1755 QSGNode *parent = itemPriv->clipNode;
1757 parent = itemPriv->opacityNode;
1759 parent = itemPriv->itemNode();
1760 QSGNode *child = itemPriv->groupNode;
1762 if (itemPriv->effectRefCount) {
1763 Q_ASSERT(itemPriv->rootNode == 0);
1764 itemPriv->rootNode = new QSGRootNode;
1767 parent->removeChildNode(child);
1768 parent->appendChildNode(itemPriv->rootNode);
1770 itemPriv->rootNode->appendChildNode(child);
1772 Q_ASSERT(itemPriv->rootNode != 0);
1773 parent->removeChildNode(itemPriv->rootNode);
1775 itemPriv->rootNode->removeChildNode(child);
1776 delete itemPriv->rootNode;
1777 itemPriv->rootNode = 0;
1779 parent->appendChildNode(child);
1783 if (dirty & QQuickItemPrivate::ChildrenUpdateMask) {
1784 QSGNode *groupNode = itemPriv->groupNode;
1786 groupNode->removeAllChildNodes();
1788 QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems();
1791 for (; ii < orderedChildren.count() && orderedChildren.at(ii)->z() < 0; ++ii) {
1792 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
1793 if (!childPrivate->explicitVisible && !childPrivate->effectRefCount)
1795 if (childPrivate->itemNode()->parent())
1796 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
1798 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
1800 itemPriv->beforePaintNode = itemPriv->groupNode ? itemPriv->groupNode->lastChild() : 0;
1802 if (itemPriv->paintNode)
1803 itemPriv->childContainerNode()->appendChildNode(itemPriv->paintNode);
1805 for (; ii < orderedChildren.count(); ++ii) {
1806 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
1807 if (!childPrivate->explicitVisible && !childPrivate->effectRefCount)
1809 if (childPrivate->itemNode()->parent())
1810 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
1812 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
1816 if ((dirty & QQuickItemPrivate::Size) && itemPriv->clipNode) {
1817 itemPriv->clipNode->setRect(item->boundingRect());
1818 itemPriv->clipNode->update();
1821 if (dirty & (QQuickItemPrivate::OpacityValue | QQuickItemPrivate::Visible | QQuickItemPrivate::HideReference)) {
1822 qreal opacity = itemPriv->explicitVisible && itemPriv->hideRefCount == 0
1823 ? itemPriv->opacity : qreal(0);
1825 if (opacity != 1 && !itemPriv->opacityNode) {
1826 itemPriv->opacityNode = new QSGOpacityNode;
1828 QSGNode *parent = itemPriv->itemNode();
1829 QSGNode *child = itemPriv->clipNode;
1831 child = itemPriv->rootNode;
1833 child = itemPriv->groupNode;
1836 parent->removeChildNode(child);
1837 parent->appendChildNode(itemPriv->opacityNode);
1839 itemPriv->opacityNode->appendChildNode(child);
1841 if (itemPriv->opacityNode)
1842 itemPriv->opacityNode->setOpacity(opacity);
1845 if (dirty & QQuickItemPrivate::ContentUpdateMask) {
1847 if (itemPriv->flags & QQuickItem::ItemHasContents) {
1848 updatePaintNodeData.transformNode = itemPriv->itemNode();
1849 itemPriv->paintNode = item->updatePaintNode(itemPriv->paintNode, &updatePaintNodeData);
1851 Q_ASSERT(itemPriv->paintNode == 0 ||
1852 itemPriv->paintNode->parent() == 0 ||
1853 itemPriv->paintNode->parent() == itemPriv->childContainerNode());
1855 if (itemPriv->paintNode && itemPriv->paintNode->parent() == 0) {
1856 if (itemPriv->beforePaintNode)
1857 itemPriv->childContainerNode()->insertChildNodeAfter(itemPriv->paintNode, itemPriv->beforePaintNode);
1859 itemPriv->childContainerNode()->prependChildNode(itemPriv->paintNode);
1861 } else if (itemPriv->paintNode) {
1862 delete itemPriv->paintNode;
1863 itemPriv->paintNode = 0;
1868 // Check consistency.
1869 const QSGNode *nodeChain[] = {
1870 itemPriv->itemNodeInstance,
1871 itemPriv->opacityNode,
1874 itemPriv->groupNode,
1875 itemPriv->paintNode,
1880 while (ip < 5 && nodeChain[ip] == 0)
1885 while (ic < 5 && nodeChain[ic] == 0)
1887 const QSGNode *parent = nodeChain[ip];
1888 const QSGNode *child = nodeChain[ic];
1890 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 0);
1892 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 1);
1893 Q_ASSERT(child->parent() == parent);
1894 bool containsChild = false;
1895 for (QSGNode *n = parent->firstChild(); n; n = n->nextSibling())
1896 containsChild |= (n == child);
1897 Q_ASSERT(containsChild);
1903 #ifdef QML_RUNTIME_TESTING
1904 if (itemPriv->sceneGraphContext()->isFlashModeEnabled()) {
1905 QSGFlashNode *flash = new QSGFlashNode();
1906 flash->setRect(item->boundingRect());
1907 itemPriv->childContainerNode()->appendChildNode(flash);
1918 void QQuickCanvas::maybeUpdate()
1922 if (d->thread && d->thread->isRunning())
1923 d->thread->maybeUpdate();
1927 \fn void QSGEngine::sceneGraphInitialized();
1929 This signal is emitted when the scene graph has been initialized.
1931 This signal will be emitted from the scene graph rendering thread.
1935 Returns the QSGEngine used for this scene.
1937 The engine will only be available once the scene graph has been
1938 initialized. Register for the sceneGraphEngine() signal to get
1939 notification about this.
1942 QSGEngine *QQuickCanvas::sceneGraphEngine() const
1944 Q_D(const QQuickCanvas);
1945 if (d->context && d->context->isReady())
1946 return d->context->engine();
1953 Sets the render target for this canvas to be \a fbo.
1955 The specified fbo must be created in the context of the canvas
1956 or one that shares with it.
1959 This function can only be called from the thread doing
1963 void QQuickCanvas::setRenderTarget(QOpenGLFramebufferObject *fbo)
1966 if (d->context && d->context && QThread::currentThread() != d->context->thread()) {
1967 qWarning("QQuickCanvas::setRenderThread: Cannot set render target from outside the rendering thread");
1971 d->renderTarget = fbo;
1977 Returns the render target for this canvas.
1979 The default is to render to the surface of the canvas, in which
1980 case the render target is 0.
1982 QOpenGLFramebufferObject *QQuickCanvas::renderTarget() const
1984 Q_D(const QQuickCanvas);
1985 return d->renderTarget;
1990 Grabs the contents of the framebuffer and returns it as an image.
1992 This function might not work if the view is not visible.
1994 \warning Calling this function will cause performance problems.
1996 \warning This function can only be called from the GUI thread.
1998 QImage QQuickCanvas::grabFrameBuffer()
2001 return d->thread ? d->thread->grab() : QImage();
2005 Returns an incubation controller that splices incubation between frames
2006 for this canvas. QQuickView automatically installs this controller for you,
2007 otherwise you will need to install it yourself using \l{QDeclarativeEngine::setIncubationController}
2009 The controller is owned by the canvas and will be destroyed when the canvas
2012 QDeclarativeIncubationController *QQuickCanvas::incubationController() const
2014 Q_D(const QQuickCanvas);
2016 if (!d->incubationController)
2017 d->incubationController = new QQuickCanvasIncubationController(const_cast<QQuickCanvasPrivate *>(d));
2018 return d->incubationController;
2022 void QQuickCanvasRenderLoop::createGLContext()
2024 gl = new QOpenGLContext();
2025 gl->setFormat(renderer->requestedFormat());
2029 void QQuickCanvasRenderThread::run()
2032 qDebug("QML Rendering Thread Started");
2038 initializeSceneGraph();
2043 while (!shouldExit) {
2046 bool sizeChanged = false;
2047 isExternalUpdatePending = false;
2049 if (renderedSize != windowSize) {
2051 printf(" RenderThread: window has changed size...\n");
2053 glViewport(0, 0, windowSize.width(), windowSize.height());
2058 printf(" RenderThread: preparing to sync...\n");
2061 if (!isGuiBlocked) {
2062 isGuiBlockPending = true;
2065 printf(" RenderThread: aquired sync lock...\n");
2067 allowMainThreadProcessingFlag = false;
2068 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
2070 printf(" RenderThread: going to sleep...\n");
2074 isGuiBlockPending = false;
2078 printf(" RenderThread: Doing locked sync\n");
2080 #ifdef QQUICK_CANVAS_TIMING
2081 if (qquick_canvas_timing)
2082 threadTimer.start();
2088 // Wake GUI after sync to let it continue animating and event processing.
2089 allowMainThreadProcessingFlag = true;
2093 printf(" RenderThread: sync done\n");
2095 #ifdef QQUICK_CANVAS_TIMING
2096 if (qquick_canvas_timing)
2097 syncTime = threadTimer.elapsed();
2101 printf(" RenderThread: rendering... %d x %d\n", windowSize.width(), windowSize.height());
2104 renderSceneGraph(windowSize);
2105 #ifdef QQUICK_CANVAS_TIMING
2106 if (qquick_canvas_timing)
2107 renderTime = threadTimer.elapsed() - syncTime;
2110 // The content of the target buffer is undefined after swap() so grab needs
2111 // to happen before swap();
2114 printf(" RenderThread: doing a grab...\n");
2116 grabContent = qt_gl_read_framebuffer(windowSize, false, false);
2121 printf(" RenderThread: wait for swap...\n");
2126 printf(" RenderThread: swap complete...\n");
2128 #ifdef QQUICK_CANVAS_TIMING
2129 if (qquick_canvas_timing) {
2130 swapTime = threadTimer.elapsed() - renderTime;
2131 qDebug() << "- Breakdown of frame time: sync:" << syncTime
2132 << "ms render:" << renderTime << "ms swap:" << swapTime
2133 << "ms total:" << swapTime + renderTime << "ms";
2138 isPaintCompleted = true;
2140 renderedSize = windowSize;
2142 // Wake the GUI thread now that rendering is complete, to signal that painting
2143 // is done, resizing is done or grabbing is completed. For grabbing, we're
2144 // signalling this much later than needed (we could have done it before swap)
2145 // but we don't want to lock an extra time.
2148 if (!animationRunning && !isExternalUpdatePending && !shouldExit && !doGrab) {
2150 printf(" RenderThread: nothing to do, going to sleep...\n");
2152 isRenderBlocked = true;
2154 isRenderBlocked = false;
2159 QCoreApplication::processEvents();
2161 // Process any "deleteLater" objects...
2162 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2166 printf(" RenderThread: deleting all outstanding nodes\n");
2168 cleanupNodesOnShutdown();
2171 printf(" RenderThread: render loop exited... Good Night!\n");
2179 printf(" RenderThread: waking GUI for final sleep..\n");
2185 printf(" RenderThread: All done...\n");
2191 bool QQuickCanvasRenderThread::event(QEvent *e)
2193 Q_ASSERT(QThread::currentThread() == qApp->thread());
2195 if (e->type() == QEvent::User) {
2196 if (!syncAlreadyHappened)
2199 syncAlreadyHappened = false;
2201 if (animationRunning && animationDriver()) {
2203 qDebug("GUI: Advancing animations...\n");
2206 animationDriver()->advance();
2209 qDebug("GUI: Animations advanced...\n");
2216 return QThread::event(e);
2221 void QQuickCanvasRenderThread::exhaustSyncEvent()
2223 if (isGuiBlockPending) {
2225 syncAlreadyHappened = true;
2231 void QQuickCanvasRenderThread::sync(bool guiAlreadyLocked)
2234 printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event");
2236 if (!guiAlreadyLocked)
2239 renderThreadAwakened = false;
2246 if (!guiAlreadyLocked)
2254 Acquires the mutex for the GUI thread. The function uses the isGuiBlocked
2255 variable to keep track of how many recursion levels the gui is locked with.
2256 We only actually acquire the mutex for the first level to avoid deadlocking
2260 void QQuickCanvasRenderThread::lockInGui()
2262 // We must avoid recursive locking in the GUI thread, hence we
2263 // only lock when we are the first one to try to block.
2270 printf("GUI: aquired lock... %d\n", isGuiBlocked);
2276 void QQuickCanvasRenderThread::unlockInGui()
2279 printf("GUI: releasing lock... %d\n", isGuiBlocked);
2289 void QQuickCanvasRenderThread::animationStarted()
2292 printf("GUI: animationStarted()\n");
2297 animationRunning = true;
2299 if (isRenderBlocked)
2307 void QQuickCanvasRenderThread::animationStopped()
2310 printf("GUI: animationStopped()...\n");
2314 animationRunning = false;
2319 void QQuickCanvasRenderThread::paint()
2322 printf("GUI: paint called..\n");
2328 isPaintCompleted = false;
2329 while (isRunning() && !isPaintCompleted) {
2330 if (isRenderBlocked)
2339 void QQuickCanvasRenderThread::resize(const QSize &size)
2342 printf("GUI: Resize Event: %dx%d\n", size.width(), size.height());
2355 while (isRunning() && renderedSize != windowSize) {
2356 if (isRenderBlocked)
2365 void QQuickCanvasRenderThread::startRendering()
2368 printf("GUI: Starting Render Thread\n");
2373 isGuiBlockPending = false;
2379 void QQuickCanvasRenderThread::stopRendering()
2382 printf("GUI: stopping render thread\n");
2389 if (isRenderBlocked) {
2391 printf("GUI: waking up render thread\n");
2396 while (!hasExited) {
2398 printf("GUI: waiting for render thread to have exited..\n");
2406 printf("GUI: waiting for render thread to terminate..\n");
2408 // Actually wait for the thread to terminate. Otherwise we can delete it
2409 // too early and crash.
2413 printf("GUI: thread has terminated and we're all good..\n");
2420 QImage QQuickCanvasRenderThread::grab()
2425 if (QThread::currentThread() != qApp->thread()) {
2426 qWarning("QQuickCanvas::grabFrameBuffer: can only be called from the GUI thread");
2431 printf("GUI: doing a pixelwise grab..\n");
2438 isPaintCompleted = false;
2439 while (isRunning() && !isPaintCompleted) {
2440 if (isRenderBlocked)
2445 QImage grabbed = grabContent;
2446 grabContent = QImage();
2455 void QQuickCanvasRenderThread::maybeUpdate()
2457 Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync,
2458 "QQuickCanvas::update",
2459 "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()");
2462 isExternalUpdatePending = true;
2464 } else if (!renderThreadAwakened) {
2466 printf("GUI: doing update...\n");
2468 renderThreadAwakened = true;
2470 isExternalUpdatePending = true;
2471 if (isRenderBlocked)
2478 #include "moc_qquickcanvas.cpp"