1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickwindow.h"
43 #include "qquickwindow_p.h"
45 #include "qquickitem.h"
46 #include "qquickitem_p.h"
47 #include "qquickevents_p_p.h"
49 #include <QtQuick/private/qsgrenderer_p.h>
50 #include <QtQuick/private/qsgtexture_p.h>
51 #include <QtQuick/private/qsgflashnode_p.h>
53 #include <private/qquickwindowmanager_p.h>
55 #include <private/qguiapplication_p.h>
56 #include <QtGui/QInputMethod>
58 #include <private/qabstractanimation_p.h>
60 #include <QtGui/qpainter.h>
61 #include <QtGui/qevent.h>
62 #include <QtGui/qmatrix4x4.h>
63 #include <QtGui/qstylehints.h>
64 #include <QtCore/qvarlengtharray.h>
65 #include <QtCore/qabstractanimation.h>
66 #include <QtQml/qqmlincubator.h>
68 #include <QtQuick/private/qquickpixmapcache_p.h>
70 #include <private/qqmlprofilerservice_p.h>
71 #include <private/qqmlmemoryprofiler_p.h>
75 void QQuickWindowPrivate::updateFocusItemTransform()
78 QQuickItem *focus = q->activeFocusItem();
79 if (focus && qApp->focusObject() == focus)
80 qApp->inputMethod()->setInputItemTransform(QQuickItemPrivate::get(focus)->itemToWindowTransform());
83 class QQuickWindowIncubationController : public QObject, public QQmlIncubationController
86 QQuickWindowIncubationController(QQuickWindowPrivate *window)
87 : m_window(window), m_eventSent(false) {}
90 virtual bool event(QEvent *e)
92 if (e->type() == QEvent::User) {
93 Q_ASSERT(m_eventSent);
94 volatile bool *amtp = m_window->windowManager->allowMainThreadProcessing();
95 while (incubatingObjectCount()) {
97 incubateWhile(amtp, 2);
100 QCoreApplication::processEvents();
105 return QObject::event(e);
108 virtual void incubatingObjectCountChanged(int count)
110 if (count && !m_eventSent) {
112 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
114 // If no animations are running, the renderer may be waiting
115 m_window->windowManager->wakeup();
119 QQuickWindowPrivate *m_window;
123 #ifndef QT_NO_ACCESSIBILITY
124 QAccessibleInterface *QQuickWindow::accessibleRoot() const
126 return QAccessible::queryAccessibleInterface(const_cast<QQuickWindow*>(this));
135 Prior to being added to a valid window items can set and clear focus with no
136 effect. Only once items are added to a window (by way of having a parent set that
137 already belongs to a window) do the focus rules apply. Focus goes back to
138 having no effect if an item is removed from a window.
140 When an item is moved into a new focus scope (either being added to a window
141 for the first time, or having its parent changed), if the focus scope already has
142 a scope focused item that takes precedence over the item being added. Otherwise,
143 the focus of the added tree is used. In the case of of a tree of items being
144 added to a window for the first time, which may have a conflicted focus state (two
145 or more items in one scope having focus set), the same rule is applied item by item -
146 thus the first item that has focus will get it (assuming the scope doesn't already
147 have a scope focused item), and the other items will have their focus cleared.
151 // #define FOCUS_DEBUG
152 // #define MOUSE_DEBUG
153 // #define TOUCH_DEBUG
154 // #define DIRTY_DEBUG
157 void printFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1);
160 QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData()
165 QQuickRootItem::QQuickRootItem()
170 void QQuickWindow::exposeEvent(QExposeEvent *)
173 d->windowManager->exposureChanged(this);
177 void QQuickWindow::resizeEvent(QResizeEvent *)
180 d->windowManager->resize(this, size());
184 void QQuickWindow::showEvent(QShowEvent *)
186 d_func()->windowManager->show(this);
190 void QQuickWindow::hideEvent(QHideEvent *)
192 d_func()->windowManager->hide(this);
196 void QQuickWindow::focusOutEvent(QFocusEvent *)
199 d->rootItem->setFocus(false);
203 void QQuickWindow::focusInEvent(QFocusEvent *)
206 d->rootItem->setFocus(true);
207 d->updateFocusItemTransform();
211 void QQuickWindowPrivate::polishItems()
213 int maxPolishCycles = 100000;
215 while (!itemsToPolish.isEmpty() && --maxPolishCycles > 0) {
216 QSet<QQuickItem *> itms = itemsToPolish;
217 itemsToPolish.clear();
219 for (QSet<QQuickItem *>::iterator it = itms.begin(); it != itms.end(); ++it) {
220 QQuickItem *item = *it;
221 QQuickItemPrivate::get(item)->polishScheduled = false;
222 item->updatePolish();
226 if (maxPolishCycles == 0)
227 qWarning("QQuickWindow: possible QQuickItem::polish() loop");
229 updateFocusItemTransform();
233 * This parameter enables that this window can be rendered without
234 * being shown on screen. This feature is very limited in what it supports.
236 * There needs to be another window actually showing that we can make current
237 * to get a surface to make current AND for this feature to be useful
238 * one needs to hook into beforeRender() and set the render tareget.
241 void QQuickWindowPrivate::setRenderWithoutShowing(bool render)
243 if (render == renderWithoutShowing)
247 renderWithoutShowing = render;
250 windowManager->show(q);
252 windowManager->hide(q);
257 * Schedules the window to render another frame.
259 * Calling QQuickWindow::update() differs from QQuickItem::update() in that
260 * it always triggers a repaint, regardless of changes in the underlying
261 * scene graph or not.
263 void QQuickWindow::update()
266 d->windowManager->update(this);
269 void forceUpdate(QQuickItem *item)
271 if (item->flags() & QQuickItem::ItemHasContents)
273 QQuickItemPrivate::get(item)->dirty(QQuickItemPrivate::ChildrenUpdateMask);
275 QList <QQuickItem *> items = item->childItems();
276 for (int i=0; i<items.size(); ++i)
277 forceUpdate(items.at(i));
280 void QQuickWindowPrivate::syncSceneGraph()
282 QML_MEMORY_SCOPE_STRING("SceneGraph");
285 emit q->beforeSynchronizing();
287 forceUpdate(rootItem);
289 QSGRootNode *rootNode = new QSGRootNode;
290 rootNode->appendChildNode(QQuickItemPrivate::get(rootItem)->itemNode());
291 renderer = context->createRenderer();
292 renderer->setRootNode(rootNode);
297 // Copy the current state of clearing from window into renderer.
298 renderer->setClearColor(clearColor);
299 QSGRenderer::ClearMode mode = QSGRenderer::ClearStencilBuffer | QSGRenderer::ClearDepthBuffer;
300 if (clearBeforeRendering)
301 mode |= QSGRenderer::ClearColorBuffer;
302 renderer->setClearMode(mode);
306 void QQuickWindowPrivate::renderSceneGraph(const QSize &size)
308 QML_MEMORY_SCOPE_STRING("SceneGraph");
310 emit q->beforeRendering();
312 renderer->setDeviceRect(QRect(QPoint(0, 0), size));
313 if (renderTargetId) {
314 fboId = renderTargetId;
315 renderer->setViewportRect(QRect(QPoint(0, 0), renderTargetSize));
317 renderer->setViewportRect(QRect(QPoint(0, 0), size));
319 renderer->setProjectionMatrixToDeviceRect();
321 context->renderNextFrame(renderer, fboId);
322 emit q->afterRendering();
325 QQuickWindowPrivate::QQuickWindowPrivate()
328 , mouseGrabberItem(0)
333 , touchMousePressTimestamp(0)
334 , renderWithoutShowing(false)
339 , clearColor(Qt::white)
340 , clearBeforeRendering(true)
341 , persistentGLContext(false)
342 , persistentSceneGraph(false)
343 , lastWheelEventAccepted(false)
346 , incubationController(0)
350 QQuickWindowPrivate::~QQuickWindowPrivate()
354 void QQuickWindowPrivate::init(QQuickWindow *c)
360 rootItem = new QQuickRootItem;
361 QQmlEngine::setObjectOwnership(rootItem, QQmlEngine::CppOwnership);
362 QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(rootItem);
363 rootItemPrivate->window = q;
364 rootItemPrivate->windowRefCount = 1;
365 rootItemPrivate->flags |= QQuickItem::ItemIsFocusScope;
367 // In the absence of a focus in event on some platforms assume the window will
368 // be activated immediately and set focus on the rootItem
369 // ### Remove when QTBUG-22415 is resolved.
370 //It is important that this call happens after the rootItem has a window..
371 rootItem->setFocus(true);
373 windowManager = QQuickWindowManager::instance();
374 context = windowManager->sceneGraphContext();
375 q->setSurfaceType(QWindow::OpenGLSurface);
376 q->setFormat(context->defaultSurfaceFormat());
378 QObject::connect(context, SIGNAL(initialized()), q, SIGNAL(sceneGraphInitialized()), Qt::DirectConnection);
379 QObject::connect(context, SIGNAL(invalidated()), q, SIGNAL(sceneGraphInvalidated()), Qt::DirectConnection);
380 QObject::connect(context, SIGNAL(invalidated()), q, SLOT(cleanupSceneGraph()), Qt::DirectConnection);
383 QQmlListProperty<QObject> QQuickWindowPrivate::data()
386 return QQuickItemPrivate::get(rootItem)->data();
389 void QQuickWindowPrivate::initRootItem()
392 q->connect(q, SIGNAL(widthChanged(int)),
393 rootItem, SLOT(setWidth(int)));
394 q->connect(q, SIGNAL(heightChanged(int)),
395 rootItem, SLOT(setHeight(int)));
396 rootItem->setWidth(q->width());
397 rootItem->setHeight(q->height());
400 static QMouseEvent *touchToMouseEvent(QEvent::Type type, const QTouchEvent::TouchPoint &p, QTouchEvent *event, QQuickItem *item, bool transformNeeded = true)
402 // The touch point local position and velocity are not yet transformed.
403 QMouseEvent *me = new QMouseEvent(type, transformNeeded ? item->mapFromScene(p.scenePos()) : p.pos(), p.scenePos(), p.screenPos(),
404 Qt::LeftButton, Qt::LeftButton, event->modifiers());
405 me->setAccepted(true);
406 me->setTimestamp(event->timestamp());
407 QVector2D transformedVelocity = p.velocity();
408 if (transformNeeded) {
409 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
410 QMatrix4x4 transformMatrix(itemPrivate->windowToItemTransform());
411 transformedVelocity = transformMatrix.mapVector(p.velocity()).toVector2D();
413 QGuiApplicationPrivate::setMouseEventCapsAndVelocity(me, event->device()->capabilities(), transformedVelocity);
417 bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *event)
420 // For each point, check if it is accepted, if not, try the next point.
421 // Any of the fingers can become the mouse one.
422 // This can happen because a mouse area might not accept an event at some point but another.
423 for (int i = 0; i < event->touchPoints().count(); ++i) {
424 const QTouchEvent::TouchPoint &p = event->touchPoints().at(i);
426 if (touchMouseId == -1 && p.state() & Qt::TouchPointPressed) {
427 QPointF pos = item->mapFromScene(p.scenePos());
429 // probably redundant, we check bounds in the calling function (matchingNewPoints)
430 if (!item->contains(pos))
433 bool doubleClick = event->timestamp() - touchMousePressTimestamp
434 < static_cast<ulong>(qApp->styleHints()->mouseDoubleClickInterval());
435 touchMousePressTimestamp = event->timestamp();
436 // Store the id already here and restore it to -1 if the event does not get
437 // accepted. Cannot defer setting the new value because otherwise if the event
438 // handler spins the event loop all subsequent moves and releases get lost.
439 touchMouseId = p.id();
440 itemForTouchPointId[touchMouseId] = item;
441 QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event, item));
443 // Send a single press and see if that's accepted
444 if (!mouseGrabberItem)
446 item->grabTouchPoints(QVector<int>() << touchMouseId);
448 q->sendEvent(item, mousePress.data());
449 event->setAccepted(mousePress->isAccepted());
450 if (!mousePress->isAccepted()) {
452 if (itemForTouchPointId.value(p.id()) == item)
453 itemForTouchPointId.remove(p.id());
455 if (mouseGrabberItem == item)
459 if (doubleClick && mousePress->isAccepted()) {
460 touchMousePressTimestamp = 0;
461 QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item));
462 q->sendEvent(item, mouseDoubleClick.data());
463 event->setAccepted(mouseDoubleClick->isAccepted());
464 if (mouseDoubleClick->isAccepted()) {
470 // The event was accepted, we are done.
471 if (mousePress->isAccepted())
473 // The event was not accepted but touchMouseId was set.
474 if (touchMouseId != -1)
476 // try the next point
478 // Touch point was there before and moved
479 } else if (p.id() == touchMouseId) {
480 if (p.state() & Qt::TouchPointMoved) {
481 if (mouseGrabberItem) {
482 QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, mouseGrabberItem));
483 q->sendEvent(mouseGrabberItem, me.data());
484 event->setAccepted(me->isAccepted());
485 if (me->isAccepted()) {
486 itemForTouchPointId[p.id()] = mouseGrabberItem; // N.B. the mouseGrabberItem may be different after returning from sendEvent()
490 // no grabber, check if we care about mouse hover
491 // FIXME: this should only happen once, not recursively... I'll ignore it just ignore hover now.
492 // hover for touch???
493 QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, item));
494 if (lastMousePosition.isNull())
495 lastMousePosition = me->windowPos();
496 QPointF last = lastMousePosition;
497 lastMousePosition = me->windowPos();
499 bool accepted = me->isAccepted();
500 bool delivered = deliverHoverEvent(rootItem, me->windowPos(), last, me->modifiers(), accepted);
502 //take care of any exits
503 accepted = clearHover();
505 me->setAccepted(accepted);
508 } else if (p.state() & Qt::TouchPointReleased) {
509 // currently handled point was released
511 if (mouseGrabberItem) {
512 QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseButtonRelease, p, event, mouseGrabberItem));
513 q->sendEvent(mouseGrabberItem, me.data());
514 if (mouseGrabberItem) // might have ungrabbed due to event
515 mouseGrabberItem->ungrabMouse();
516 return me->isAccepted();
525 void QQuickWindowPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform)
527 QMatrix4x4 transformMatrix(transform);
528 for (int i=0; i<touchPoints.count(); i++) {
529 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
530 touchPoint.setRect(transform.mapRect(touchPoint.sceneRect()));
531 touchPoint.setStartPos(transform.map(touchPoint.startScenePos()));
532 touchPoint.setLastPos(transform.map(touchPoint.lastScenePos()));
533 touchPoint.setVelocity(transformMatrix.mapVector(touchPoint.velocity()).toVector2D());
539 Translates the data in \a touchEvent to this window. This method leaves the item local positions in
540 \a touchEvent untouched (these are filled in later).
542 void QQuickWindowPrivate::translateTouchEvent(QTouchEvent *touchEvent)
544 QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
545 for (int i = 0; i < touchPoints.count(); ++i) {
546 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
548 touchPoint.setScreenRect(touchPoint.sceneRect());
549 touchPoint.setStartScreenPos(touchPoint.startScenePos());
550 touchPoint.setLastScreenPos(touchPoint.lastScenePos());
552 touchPoint.setSceneRect(touchPoint.rect());
553 touchPoint.setStartScenePos(touchPoint.startPos());
554 touchPoint.setLastScenePos(touchPoint.lastPos());
557 lastMousePosition = touchPoint.pos().toPoint();
559 touchEvent->setTouchPoints(touchPoints);
562 void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
567 Q_ASSERT(scope || item == rootItem);
570 qWarning() << "QQuickWindowPrivate::setFocusInScope():";
571 qWarning() << " scope:" << (QObject *)scope;
573 qWarning() << " scopeSubFocusItem:" << (QObject *)QQuickItemPrivate::get(scope)->subFocusItem;
574 qWarning() << " item:" << (QObject *)item;
575 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
578 QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0;
579 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
581 QQuickItem *oldActiveFocusItem = 0;
582 QQuickItem *newActiveFocusItem = 0;
584 QVarLengthArray<QQuickItem *, 20> changed;
586 // Does this change the active focus?
587 if (item == rootItem || (scopePrivate->activeFocus && item->isEnabled())) {
588 oldActiveFocusItem = activeFocusItem;
589 newActiveFocusItem = item;
590 while (newActiveFocusItem->isFocusScope()
591 && newActiveFocusItem->scopedFocusItem()
592 && newActiveFocusItem->scopedFocusItem()->isEnabled()) {
593 newActiveFocusItem = newActiveFocusItem->scopedFocusItem();
596 if (oldActiveFocusItem) {
598 qApp->inputMethod()->commit();
602 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
603 q->sendEvent(oldActiveFocusItem, &event);
605 QQuickItem *afi = oldActiveFocusItem;
606 while (afi && afi != scope) {
607 if (QQuickItemPrivate::get(afi)->activeFocus) {
608 QQuickItemPrivate::get(afi)->activeFocus = false;
611 afi = afi->parentItem();
616 if (item != rootItem && !(options & DontChangeSubFocusItem)) {
617 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
618 if (oldSubFocusItem) {
619 QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
620 changed << oldSubFocusItem;
623 QQuickItemPrivate::get(item)->updateSubFocusItem(scope, true);
626 if (!(options & DontChangeFocusProperty)) {
627 // if (item != rootItem || QGuiApplication::focusWindow() == q) { // QTBUG-22415
628 itemPrivate->focus = true;
633 if (newActiveFocusItem && rootItem->hasFocus()) {
634 activeFocusItem = newActiveFocusItem;
636 QQuickItemPrivate::get(newActiveFocusItem)->activeFocus = true;
637 changed << newActiveFocusItem;
639 QQuickItem *afi = newActiveFocusItem->parentItem();
640 while (afi && afi != scope) {
641 if (afi->isFocusScope()) {
642 QQuickItemPrivate::get(afi)->activeFocus = true;
645 afi = afi->parentItem();
648 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
649 q->sendEvent(newActiveFocusItem, &event);
652 emit q->focusObjectChanged(activeFocusItem);
654 if (!changed.isEmpty())
655 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
658 void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
663 Q_ASSERT(scope || item == rootItem);
666 qWarning() << "QQuickWindowPrivate::clearFocusInScope():";
667 qWarning() << " scope:" << (QObject *)scope;
668 qWarning() << " item:" << (QObject *)item;
669 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
672 QQuickItemPrivate *scopePrivate = 0;
674 scopePrivate = QQuickItemPrivate::get(scope);
675 if ( !scopePrivate->subFocusItem )
676 return;//No focus, nothing to do.
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->inputMethod()->commit();
698 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
699 q->sendEvent(oldActiveFocusItem, &event);
701 QQuickItem *afi = oldActiveFocusItem;
702 while (afi && afi != scope) {
703 if (QQuickItemPrivate::get(afi)->activeFocus) {
704 QQuickItemPrivate::get(afi)->activeFocus = false;
707 afi = afi->parentItem();
711 if (item != rootItem && !(options & DontChangeSubFocusItem)) {
712 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
713 if (oldSubFocusItem && !(options & DontChangeFocusProperty)) {
714 QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
715 changed << oldSubFocusItem;
718 QQuickItemPrivate::get(item)->updateSubFocusItem(scope, false);
720 } else if (!(options & DontChangeFocusProperty)) {
721 QQuickItemPrivate::get(item)->focus = false;
725 if (newActiveFocusItem) {
726 Q_ASSERT(newActiveFocusItem == scope);
727 activeFocusItem = scope;
729 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
730 q->sendEvent(newActiveFocusItem, &event);
733 emit q->focusObjectChanged(activeFocusItem);
735 if (!changed.isEmpty())
736 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
739 void QQuickWindowPrivate::notifyFocusChangesRecur(QQuickItem **items, int remaining)
741 QQmlGuard<QQuickItem> item(*items);
744 notifyFocusChangesRecur(items + 1, remaining - 1);
747 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
749 if (itemPrivate->notifiedFocus != itemPrivate->focus) {
750 itemPrivate->notifiedFocus = itemPrivate->focus;
751 emit item->focusChanged(itemPrivate->focus);
754 if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) {
755 itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus;
756 itemPrivate->itemChange(QQuickItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus);
757 emit item->activeFocusChanged(itemPrivate->activeFocus);
762 void QQuickWindowPrivate::dirtyItem(QQuickItem *)
768 void QQuickWindowPrivate::cleanup(QSGNode *n)
772 Q_ASSERT(!cleanupNodeList.contains(n));
773 cleanupNodeList.append(n);
779 \instantiates QQuickWindow
780 \inqmlmodule QtQuick.Window 2
781 \ingroup qtquick-visual
782 \brief Creates a new top-level window
784 The Window object creates a new top-level window for a QtQuick scene. It automatically sets up the
785 window for use with QtQuick 2.0 graphical types.
787 To use this type, you will need to import the module with the following line:
789 import QtQuick.Window 2.0
792 Restricting this import will allow you to have a QML environment without access to window system features.
800 \brief The QQuickWindow class provides the window for displaying a graphical QML scene
802 QQuickWindow provides the graphical scene management needed to interact with and display
803 a scene of QQuickItems.
805 A QQuickWindow always has a single invisible root item. To add items to this window,
806 reparent the items to the root item or to an existing item in the scene.
808 For easily displaying a scene from a QML file, see \l{QQuickView}.
810 \section1 Scene Graph and Rendering
812 The QQuickWindow uses a scene graph on top of OpenGL to render. This scene graph is disconnected
813 from the QML scene and potentially lives in another thread, depending on the platform
814 implementation. Since the rendering scene graph lives independently from the QML scene, it can
815 also be completely released without affecting the state of the QML scene.
817 The sceneGraphInitialized() signal is emitted on the rendering thread before the QML scene is
818 rendered to the screen for the first time. If the rendering scene graph has been released
819 the signal will be emitted again before the next frame is rendered.
821 The rendering of each frame is broken down into the following
822 steps, in the given order:
826 \li The QQuickWindow::beforeSynchronizing() signal is emitted.
827 Applications can make direct connections (Qt::DirectConnection)
828 to this signal to do any preparation required before calls to
829 QQuickItem::updatePaintNode().
831 \li Synchronization of the QML state into the scene graph. This is
832 done by calling the QQuickItem::updatePaintNode() function on all
833 items that have changed since the previous frame. When a dedicated
834 rendering thread is used, the GUI thread is blocked during this
835 synchroniation. This is the only time the QML items and the nodes
836 in the scene graph interact.
838 \li The window to be rendered is made current using
839 QOpenGLContext::makeCurrent().
841 \li The QQuickWindow::beforeRendering() signal is
842 emitted. Applications can make direct connections
843 (Qt::DirectConnection) to this signal to use custom OpenGL calls
844 which will then stack visually beneath the QML scene.
846 \li Items that have specified QSGNode::UsesPreprocess, will have their
847 QSGNode::preprocess() function invoked.
849 \li The QQuickWindow is cleared according to what is specified
850 using QQuickWindow::setClearBeforeRendering() and
851 QQuickWindow::setClearColor().
853 \li The scene graph is rendered.
855 \li The QQuickWindow::afterRendering() signal is
856 emitted. Applications can make direct connections
857 (Qt::DirectConnection) to this signal to use custom OpenGL calls
858 which will then stack visually over the QML scene.
860 \li The rendered frame is swapped and QQuickWindow::frameSwapped()
865 All of the above happen on the rendering thread, when applicable.
867 While the scene graph is being rendered on the rendering thread, the GUI will process animations
868 for the next frame. This means that as long as users are not using scene graph API
869 directly, the added complexity of a rendering thread can be completely ignored.
871 When a QQuickWindow is programatically hidden with hide() or setVisible(false), it will
872 stop rendering and its scene graph and OpenGL context might be released. The
873 sceneGraphInvalidated() signal will be emitted when this happens.
875 \warning It is crucial that OpenGL operations and interaction with the scene graph happens
876 exclusively on the rendering thread, primarily during the updatePaintNode() phase.
878 \warning As signals related to rendering might be emitted from the rendering thread,
879 connections should be made using Qt::DirectConnection
882 \section2 Resource Management
884 QML will typically try to cache images, scene graph nodes, etc to improve performance, but in
885 some low-memory scenarios it might be required to aggressively release these resources. The
886 releaseResources() can be used to force clean up of certain resources. Calling releaseResources()
887 may result in the entire scene graph and its OpenGL context being deleted. The
888 sceneGraphInvalidated() signal will be emitted when this happens.
890 \sa {OpenGL Under QML Example}
893 QQuickWindow::QQuickWindow(QWindow *parent)
894 : QWindow(*(new QQuickWindowPrivate), parent)
900 QQuickWindow::QQuickWindow(QQuickWindowPrivate &dd, QWindow *parent)
901 : QWindow(dd, parent)
907 QQuickWindow::~QQuickWindow()
911 d->windowManager->windowDestroyed(this);
913 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
914 delete d->incubationController; d->incubationController = 0;
916 delete d->rootItem; d->rootItem = 0;
922 This function tries to release redundant resources currently held by the QML scene.
924 Calling this function might result in the scene graph and the OpenGL context used
925 for rendering being released to release graphics memory. If this happens, the
926 sceneGraphInvalidated() signal will be called, allowing users to clean up their
927 own graphics resources. The setPersistentOpenGLContext() and setPersistentSceneGraph()
928 functions can be used to prevent this from happening, if handling the cleanup is
929 not feasible in the application, at the cost of higher memory usage.
931 \sa sceneGraphInvalidated(), setPersistentOpenGLContext(), setPersistentSceneGraph()
934 void QQuickWindow::releaseResources()
937 d->windowManager->releaseResources();
938 QQuickPixmap::purgeCache();
944 Controls whether the OpenGL context can be released as a part of a call to
947 The OpenGL context might still be released when the user makes an explicit
950 \sa setPersistentSceneGraph()
953 void QQuickWindow::setPersistentOpenGLContext(bool persistent)
956 d->persistentGLContext = persistent;
961 Returns whether the OpenGL context can be released as a part of a call to
965 bool QQuickWindow::isPersistentOpenGLContext() const
967 Q_D(const QQuickWindow);
968 return d->persistentGLContext;
974 Controls whether the scene graph nodes and resources can be released as a
975 part of a call to releaseResources().
977 The scene graph nodes and resources might still be released when the user
978 makes an explicit call to hide().
980 \sa setPersistentOpenGLContext()
983 void QQuickWindow::setPersistentSceneGraph(bool persistent)
986 d->persistentSceneGraph = persistent;
992 Returns whether the scene graph nodes and resources can be released as a part
993 of a call to releaseResources().
996 bool QQuickWindow::isPersistentSceneGraph() const
998 Q_D(const QQuickWindow);
999 return d->persistentSceneGraph;
1007 Returns the invisible root item of the scene.
1009 A QQuickWindow always has a single invisible root item containing all of its content.
1010 To add items to this window, reparent the items to the contentItem or to an existing
1013 QQuickItem *QQuickWindow::contentItem() const
1015 Q_D(const QQuickWindow);
1021 Returns the item which currently has active focus.
1023 QQuickItem *QQuickWindow::activeFocusItem() const
1025 Q_D(const QQuickWindow);
1027 return d->activeFocusItem;
1034 QObject *QQuickWindow::focusObject() const
1036 Q_D(const QQuickWindow);
1038 if (d->activeFocusItem)
1039 return d->activeFocusItem;
1040 return const_cast<QQuickWindow*>(this);
1045 Returns the item which currently has the mouse grab.
1047 QQuickItem *QQuickWindow::mouseGrabberItem() const
1049 Q_D(const QQuickWindow);
1051 return d->mouseGrabberItem;
1056 \qmlproperty color QtQuick.Window2::Window::color
1058 The background color for the window.
1060 Setting this property is more efficient than using a separate Rectangle.
1063 bool QQuickWindowPrivate::clearHover()
1066 if (hoverItems.isEmpty())
1069 QPointF pos = q->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint());
1071 bool accepted = false;
1072 foreach (QQuickItem* item, hoverItems)
1073 accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), true) || accepted;
1079 bool QQuickWindow::event(QEvent *e)
1083 switch (e->type()) {
1085 case QEvent::TouchBegin:
1086 case QEvent::TouchUpdate:
1087 case QEvent::TouchEnd: {
1088 QTouchEvent *touch = static_cast<QTouchEvent*>(e);
1089 d->translateTouchEvent(touch);
1090 // return in order to avoid the QWindow::event below
1091 return d->deliverTouchEvent(touch);
1094 case QEvent::TouchCancel:
1095 // return in order to avoid the QWindow::event below
1096 return d->deliverTouchCancelEvent(static_cast<QTouchEvent*>(e));
1100 d->lastMousePosition = QPoint();
1102 #ifndef QT_NO_DRAGANDDROP
1103 case QEvent::DragEnter:
1104 case QEvent::DragLeave:
1105 case QEvent::DragMove:
1107 d->deliverDragEvent(&d->dragGrabber, e);
1110 case QEvent::WindowDeactivate:
1111 rootItem()->windowDeactivateEvent();
1113 case QEvent::FocusAboutToChange:
1114 if (d->activeFocusItem)
1115 qGuiApp->inputMethod()->commit();
1121 return QWindow::event(e);
1125 void QQuickWindow::keyPressEvent(QKeyEvent *e)
1129 if (d->activeFocusItem)
1130 sendEvent(d->activeFocusItem, e);
1134 void QQuickWindow::keyReleaseEvent(QKeyEvent *e)
1138 if (d->activeFocusItem)
1139 sendEvent(d->activeFocusItem, e);
1142 QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos)
1144 int caps = QGuiApplicationPrivate::mouseEventCaps(event);
1145 QVector2D velocity = QGuiApplicationPrivate::mouseEventVelocity(event);
1146 QMouseEvent *me = new QMouseEvent(event->type(),
1147 transformedLocalPos ? *transformedLocalPos : event->localPos(),
1148 event->windowPos(), event->screenPos(),
1149 event->button(), event->buttons(), event->modifiers());
1150 QGuiApplicationPrivate::setMouseEventCapsAndVelocity(me, caps, velocity);
1151 me->setTimestamp(event->timestamp());
1155 bool QQuickWindowPrivate::deliverInitialMousePressEvent(QQuickItem *item, QMouseEvent *event)
1159 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1161 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1162 QPointF p = item->mapFromScene(event->windowPos());
1163 if (!item->contains(p))
1167 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1168 for (int ii = children.count() - 1; ii >= 0; --ii) {
1169 QQuickItem *child = children.at(ii);
1170 if (!child->isVisible() || !child->isEnabled())
1172 if (deliverInitialMousePressEvent(child, event))
1176 if (itemPrivate->acceptedMouseButtons() & event->button()) {
1177 QPointF localPos = item->mapFromScene(event->windowPos());
1178 if (item->contains(localPos)) {
1179 QScopedPointer<QMouseEvent> me(cloneMouseEvent(event, &localPos));
1182 q->sendEvent(item, me.data());
1183 event->setAccepted(me->isAccepted());
1184 if (me->isAccepted())
1186 if (mouseGrabberItem && !event->buttons())
1187 mouseGrabberItem->ungrabMouse();
1194 bool QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event)
1198 lastMousePosition = event->windowPos();
1200 if (!mouseGrabberItem &&
1201 event->type() == QEvent::MouseButtonPress &&
1202 (event->buttons() & event->button()) == event->buttons()) {
1203 if (deliverInitialMousePressEvent(rootItem, event))
1207 return event->isAccepted();
1210 if (mouseGrabberItem) {
1211 QPointF localPos = mouseGrabberItem->mapFromScene(event->windowPos());
1212 QScopedPointer<QMouseEvent> me(cloneMouseEvent(event, &localPos));
1214 q->sendEvent(mouseGrabberItem, me.data());
1215 event->setAccepted(me->isAccepted());
1216 if (me->isAccepted())
1224 void QQuickWindow::mousePressEvent(QMouseEvent *event)
1228 qWarning() << "QQuickWindow::mousePressEvent()" << event->localPos() << event->button() << event->buttons();
1231 d->deliverMouseEvent(event);
1235 void QQuickWindow::mouseReleaseEvent(QMouseEvent *event)
1239 qWarning() << "QQuickWindow::mouseReleaseEvent()" << event->localPos() << event->button() << event->buttons();
1242 if (!d->mouseGrabberItem) {
1243 QWindow::mouseReleaseEvent(event);
1247 d->deliverMouseEvent(event);
1248 if (d->mouseGrabberItem && !event->buttons())
1249 d->mouseGrabberItem->ungrabMouse();
1253 void QQuickWindow::mouseDoubleClickEvent(QMouseEvent *event)
1257 qWarning() << "QQuickWindow::mouseDoubleClickEvent()" << event->localPos() << event->button() << event->buttons();
1260 if (!d->mouseGrabberItem && (event->buttons() & event->button()) == event->buttons()) {
1261 if (d->deliverInitialMousePressEvent(d->rootItem, event))
1268 d->deliverMouseEvent(event);
1271 bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
1272 const QPointF &scenePos, const QPointF &lastScenePos,
1273 Qt::KeyboardModifiers modifiers, bool accepted)
1276 const QTransform transform = QQuickItemPrivate::get(item)->windowToItemTransform();
1278 //create copy of event
1279 QHoverEvent hoverEvent(type, transform.map(scenePos), transform.map(lastScenePos), modifiers);
1280 hoverEvent.setAccepted(accepted);
1282 q->sendEvent(item, &hoverEvent);
1284 return hoverEvent.isAccepted();
1288 void QQuickWindow::mouseMoveEvent(QMouseEvent *event)
1292 qWarning() << "QQuickWindow::mouseMoveEvent()" << event->localPos() << event->button() << event->buttons();
1295 #ifndef QT_NO_CURSOR
1296 d->updateCursor(event->windowPos());
1299 if (!d->mouseGrabberItem) {
1300 if (d->lastMousePosition.isNull())
1301 d->lastMousePosition = event->windowPos();
1302 QPointF last = d->lastMousePosition;
1303 d->lastMousePosition = event->windowPos();
1305 bool accepted = event->isAccepted();
1306 bool delivered = d->deliverHoverEvent(d->rootItem, event->windowPos(), last, event->modifiers(), accepted);
1308 //take care of any exits
1309 accepted = d->clearHover();
1311 event->setAccepted(accepted);
1315 d->deliverMouseEvent(event);
1318 bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
1319 Qt::KeyboardModifiers modifiers, bool &accepted)
1321 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1323 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1324 QPointF p = item->mapFromScene(scenePos);
1325 if (!item->contains(p))
1329 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1330 for (int ii = children.count() - 1; ii >= 0; --ii) {
1331 QQuickItem *child = children.at(ii);
1332 if (!child->isVisible() || !child->isEnabled())
1334 if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, accepted))
1338 if (itemPrivate->hoverEnabled) {
1339 QPointF p = item->mapFromScene(scenePos);
1340 if (item->contains(p)) {
1341 if (!hoverItems.isEmpty() && hoverItems[0] == item) {
1343 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1345 QList<QQuickItem *> itemsToHover;
1346 QQuickItem* parent = item;
1347 itemsToHover << item;
1348 while ((parent = parent->parentItem()))
1349 itemsToHover << parent;
1351 // Leaving from previous hovered items until we reach the item or one of its ancestors.
1352 while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems[0])) {
1353 sendHoverEvent(QEvent::HoverLeave, hoverItems[0], scenePos, lastScenePos, modifiers, accepted);
1354 hoverItems.removeFirst();
1357 if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item
1358 // ### Shouldn't we send moves for the parent items as well?
1359 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1361 // Enter items that are not entered yet.
1363 if (!hoverItems.isEmpty())
1364 startIdx = itemsToHover.indexOf(hoverItems[0]) - 1;
1366 startIdx = itemsToHover.count() - 1;
1368 for (int i = startIdx; i >= 0; i--) {
1369 QQuickItem *itemToHover = itemsToHover[i];
1370 if (QQuickItemPrivate::get(itemToHover)->hoverEnabled) {
1371 hoverItems.prepend(itemToHover);
1372 sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, accepted);
1384 bool QQuickWindowPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event)
1387 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1389 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1390 QPointF p = item->mapFromScene(event->posF());
1391 if (!item->contains(p))
1395 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1396 for (int ii = children.count() - 1; ii >= 0; --ii) {
1397 QQuickItem *child = children.at(ii);
1398 if (!child->isVisible() || !child->isEnabled())
1400 if (deliverWheelEvent(child, event))
1404 QPointF p = item->mapFromScene(event->posF());
1406 if (item->contains(p)) {
1407 QWheelEvent wheel(p, p, event->pixelDelta(), event->angleDelta(), event->delta(),
1408 event->orientation(), event->buttons(), event->modifiers());
1410 q->sendEvent(item, &wheel);
1411 if (wheel.isAccepted()) {
1420 #ifndef QT_NO_WHEELEVENT
1422 void QQuickWindow::wheelEvent(QWheelEvent *event)
1426 qWarning() << "QQuickWindow::wheelEvent()" << event->pixelDelta() << event->angleDelta();
1429 //if the actual wheel event was accepted, accept the compatability wheel event and return early
1430 if (d->lastWheelEventAccepted && event->angleDelta().isNull())
1434 d->deliverWheelEvent(d->rootItem, event);
1435 d->lastWheelEventAccepted = event->isAccepted();
1437 #endif // QT_NO_WHEELEVENT
1440 bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event)
1443 qWarning("touchCancelEvent");
1446 // A TouchCancel event will typically not contain any points.
1447 // Deliver it to all items that have active touches.
1448 QSet<QQuickItem *> cancelDelivered;
1449 foreach (QQuickItem *item, itemForTouchPointId) {
1450 if (cancelDelivered.contains(item))
1452 cancelDelivered.insert(item);
1453 q->sendEvent(item, event);
1456 if (mouseGrabberItem)
1457 mouseGrabberItem->ungrabMouse();
1458 // The next touch event can only be a TouchBegin so clean up.
1459 itemForTouchPointId.clear();
1463 // check what kind of touch we have (begin/update) and
1464 // call deliverTouchPoints to actually dispatch the points
1465 bool QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event)
1468 if (event->type() == QEvent::TouchBegin)
1469 qWarning() << "touchBeginEvent";
1470 else if (event->type() == QEvent::TouchUpdate)
1471 qWarning() << "touchUpdateEvent points";
1472 else if (event->type() == QEvent::TouchEnd)
1473 qWarning("touchEndEvent");
1476 // List of all items that received an event before
1477 // When we have TouchBegin this is and will stay empty
1478 QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > updatedPoints;
1480 // Figure out who accepted a touch point last and put it in updatedPoints
1481 // Add additional item to newPoints
1482 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
1483 QList<QTouchEvent::TouchPoint> newPoints;
1484 for (int i=0; i<touchPoints.count(); i++) {
1485 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
1486 if (touchPoint.state() == Qt::TouchPointPressed) {
1487 newPoints << touchPoint;
1489 // TouchPointStationary is relevant only to items which
1490 // are also receiving touch points with some other state.
1491 // But we have not yet decided which points go to which item,
1492 // so for now we must include all non-new points in updatedPoints.
1493 if (itemForTouchPointId.contains(touchPoint.id())) {
1494 QQuickItem *item = itemForTouchPointId.value(touchPoint.id());
1496 updatedPoints[item].append(touchPoint);
1501 // Deliver the event, but only if there is at least one new point
1502 // or some item accepted a point and should receive an update
1503 if (newPoints.count() > 0 || updatedPoints.count() > 0) {
1504 QSet<int> acceptedNewPoints;
1505 event->setAccepted(deliverTouchPoints(rootItem, event, newPoints, &acceptedNewPoints, &updatedPoints));
1509 // Remove released points from itemForTouchPointId
1510 if (event->touchPointStates() & Qt::TouchPointReleased) {
1511 for (int i=0; i<touchPoints.count(); i++) {
1512 if (touchPoints[i].state() == Qt::TouchPointReleased) {
1513 itemForTouchPointId.remove(touchPoints[i].id());
1514 if (touchPoints[i].id() == touchMouseId)
1520 if (event->type() == QEvent::TouchEnd) {
1521 Q_ASSERT(itemForTouchPointId.isEmpty());
1524 return event->isAccepted();
1527 // This function recurses and sends the events to the individual items
1528 bool QQuickWindowPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints, QSet<int> *acceptedNewPoints, QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *updatedPoints)
1530 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1532 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1533 for (int i=0; i<newPoints.count(); i++) {
1534 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1535 if (!item->contains(p))
1540 // Check if our children want the event (or parts of it)
1541 // This is the only point where touch event delivery recurses!
1542 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1543 for (int ii = children.count() - 1; ii >= 0; --ii) {
1544 QQuickItem *child = children.at(ii);
1545 if (!child->isEnabled() || !child->isVisible())
1547 if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints))
1551 // None of the children accepted the event, so check the given item itself.
1552 // First, construct matchingPoints as a list of TouchPoints which the
1553 // given item might be interested in. Any newly-pressed point which is
1554 // inside the item's bounds will be interesting, and also any updated point
1555 // which was already accepted by that item when it was first pressed.
1556 // (A point which was already accepted is effectively "grabbed" by the item.)
1558 // set of IDs of "interesting" new points
1559 QSet<int> matchingNewPoints;
1560 // set of points which this item has previously accepted, for starters
1561 QList<QTouchEvent::TouchPoint> matchingPoints = (*updatedPoints)[item];
1562 // now add the new points which are inside this item's bounds
1563 if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) {
1564 for (int i = 0; i < newPoints.count(); i++) {
1565 if (acceptedNewPoints->contains(newPoints[i].id()))
1567 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1568 if (item->contains(p)) {
1569 matchingNewPoints.insert(newPoints[i].id());
1570 matchingPoints << newPoints[i];
1574 // If there are no matching new points, and the existing points are all stationary,
1575 // there's no need to send an event to this item. This is required by a test in
1576 // tst_qquickwindow::touchEvent_basic:
1577 // a single stationary press on an item shouldn't cause an event
1578 if (matchingNewPoints.isEmpty()) {
1579 bool stationaryOnly = true;
1580 Q_FOREACH (QTouchEvent::TouchPoint tp, matchingPoints)
1581 if (tp.state() != Qt::TouchPointStationary)
1582 stationaryOnly = false;
1584 matchingPoints.clear();
1587 if (!matchingPoints.isEmpty()) {
1588 // Now we know this item might be interested in the event. Copy and send it, but
1589 // with only the subset of TouchPoints which are relevant to that item: that's matchingPoints.
1590 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1591 transformTouchPoints(matchingPoints, itemPrivate->windowToItemTransform());
1592 deliverMatchingPointsToItem(item, event, acceptedNewPoints, matchingNewPoints, matchingPoints);
1595 // record the fact that this item has been visited already
1596 updatedPoints->remove(item);
1598 // recursion is done only if ALL touch points have been delivered
1599 return (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty());
1602 // touchEventForItemBounds has no means to generate a touch event that contains
1603 // only the points that are relevant for this item. Thus the need for
1604 // matchingPoints to already be that set of interesting points.
1605 // They are all pre-transformed, too.
1606 bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints)
1608 QScopedPointer<QTouchEvent> touchEvent(touchEventWithPoints(*event, matchingPoints));
1609 touchEvent.data()->setTarget(item);
1610 bool touchEventAccepted = false;
1612 // First check whether the parent wants to be a filter,
1613 // and if the parent accepts the event we are done.
1614 if (sendFilteredTouchEvent(item->parentItem(), item, event)) {
1619 // Since it can change in sendEvent, update itemForTouchPointId now
1620 foreach (int id, matchingNewPoints)
1621 itemForTouchPointId[id] = item;
1623 // Deliver the touch event to the given item
1624 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1625 itemPrivate->deliverTouchEvent(touchEvent.data());
1626 touchEventAccepted = touchEvent->isAccepted();
1628 // If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it.
1629 if (!touchEventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) {
1631 event->setAccepted(translateTouchToMouse(item, event));
1632 if (event->isAccepted()) {
1633 touchEventAccepted = true;
1637 if (touchEventAccepted) {
1638 // If the touch was accepted (regardless by whom or in what form),
1639 // update acceptedNewPoints.
1640 foreach (int id, matchingNewPoints)
1641 acceptedNewPoints->insert(id);
1643 // But if the event was not accepted then we know this item
1644 // will not be interested in further updates for those touchpoint IDs either.
1645 foreach (int id, matchingNewPoints)
1646 if (itemForTouchPointId[id] == item)
1647 itemForTouchPointId.remove(id);
1650 return touchEventAccepted;
1653 QTouchEvent *QQuickWindowPrivate::touchEventForItemBounds(QQuickItem *target, const QTouchEvent &originalEvent)
1655 const QList<QTouchEvent::TouchPoint> &touchPoints = originalEvent.touchPoints();
1656 QList<QTouchEvent::TouchPoint> pointsInBounds;
1657 // if all points are stationary, the list of points should be empty to signal a no-op
1658 if (originalEvent.touchPointStates() != Qt::TouchPointStationary) {
1659 for (int i = 0; i < touchPoints.count(); ++i) {
1660 const QTouchEvent::TouchPoint &tp = touchPoints.at(i);
1661 if (tp.state() == Qt::TouchPointPressed) {
1662 QPointF p = target->mapFromScene(tp.scenePos());
1663 if (target->contains(p))
1664 pointsInBounds.append(tp);
1666 pointsInBounds.append(tp);
1669 transformTouchPoints(pointsInBounds, QQuickItemPrivate::get(target)->windowToItemTransform());
1672 QTouchEvent* touchEvent = touchEventWithPoints(originalEvent, pointsInBounds);
1673 touchEvent->setTarget(target);
1677 QTouchEvent *QQuickWindowPrivate::touchEventWithPoints(const QTouchEvent &event, const QList<QTouchEvent::TouchPoint> &newPoints)
1679 Qt::TouchPointStates eventStates;
1680 for (int i=0; i<newPoints.count(); i++)
1681 eventStates |= newPoints[i].state();
1682 // if all points have the same state, set the event type accordingly
1683 QEvent::Type eventType = event.type();
1684 switch (eventStates) {
1685 case Qt::TouchPointPressed:
1686 eventType = QEvent::TouchBegin;
1688 case Qt::TouchPointReleased:
1689 eventType = QEvent::TouchEnd;
1692 eventType = QEvent::TouchUpdate;
1696 QTouchEvent *touchEvent = new QTouchEvent(eventType);
1697 touchEvent->setWindow(event.window());
1698 touchEvent->setTarget(event.target());
1699 touchEvent->setDevice(event.device());
1700 touchEvent->setModifiers(event.modifiers());
1701 touchEvent->setTouchPoints(newPoints);
1702 touchEvent->setTouchPointStates(eventStates);
1703 touchEvent->setTimestamp(event.timestamp());
1704 touchEvent->accept();
1708 #ifndef QT_NO_DRAGANDDROP
1709 void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event)
1712 grabber->resetTarget();
1713 QQuickDragGrabber::iterator grabItem = grabber->begin();
1714 if (grabItem != grabber->end()) {
1715 Q_ASSERT(event->type() != QEvent::DragEnter);
1716 if (event->type() == QEvent::Drop) {
1717 QDropEvent *e = static_cast<QDropEvent *>(event);
1718 for (e->setAccepted(false); !e->isAccepted() && grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1719 QPointF p = (**grabItem)->mapFromScene(e->pos());
1720 QDropEvent translatedEvent(
1722 e->possibleActions(),
1725 e->keyboardModifiers());
1726 QQuickDropEventEx::copyActions(&translatedEvent, *e);
1727 q->sendEvent(**grabItem, &translatedEvent);
1728 e->setAccepted(translatedEvent.isAccepted());
1729 e->setDropAction(translatedEvent.dropAction());
1730 grabber->setTarget(**grabItem);
1733 if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave.
1734 QDragLeaveEvent leaveEvent;
1735 for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
1736 q->sendEvent(**grabItem, &leaveEvent);
1738 } else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1739 QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event);
1740 if (deliverDragEvent(grabber, **grabItem, moveEvent)) {
1741 moveEvent->setAccepted(true);
1742 for (++grabItem; grabItem != grabber->end();) {
1743 QPointF p = (**grabItem)->mapFromScene(moveEvent->pos());
1744 if ((**grabItem)->contains(p)) {
1745 QDragMoveEvent translatedEvent(
1747 moveEvent->possibleActions(),
1748 moveEvent->mimeData(),
1749 moveEvent->mouseButtons(),
1750 moveEvent->keyboardModifiers());
1751 QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent);
1752 q->sendEvent(**grabItem, &translatedEvent);
1755 QDragLeaveEvent leaveEvent;
1756 q->sendEvent(**grabItem, &leaveEvent);
1757 grabItem = grabber->release(grabItem);
1762 QDragLeaveEvent leaveEvent;
1763 q->sendEvent(**grabItem, &leaveEvent);
1767 if (event->type() == QEvent::DragEnter || event->type() == QEvent::DragMove) {
1768 QDragMoveEvent *e = static_cast<QDragMoveEvent *>(event);
1769 QDragEnterEvent enterEvent(
1771 e->possibleActions(),
1774 e->keyboardModifiers());
1775 QQuickDropEventEx::copyActions(&enterEvent, *e);
1776 event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent));
1780 bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event)
1783 bool accepted = false;
1784 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1785 if (!item->isVisible() || !item->isEnabled())
1788 QPointF p = item->mapFromScene(event->pos());
1789 if (item->contains(p)) {
1790 if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) {
1791 QDragMoveEvent translatedEvent(
1793 event->possibleActions(),
1795 event->mouseButtons(),
1796 event->keyboardModifiers(),
1798 QQuickDropEventEx::copyActions(&translatedEvent, *event);
1799 q->sendEvent(item, &translatedEvent);
1800 if (event->type() == QEvent::DragEnter) {
1801 if (translatedEvent.isAccepted()) {
1802 grabber->grab(item);
1809 } else if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1813 QDragEnterEvent enterEvent(
1815 event->possibleActions(),
1817 event->mouseButtons(),
1818 event->keyboardModifiers());
1819 QQuickDropEventEx::copyActions(&enterEvent, *event);
1820 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1821 for (int ii = children.count() - 1; ii >= 0; --ii) {
1822 if (deliverDragEvent(grabber, children.at(ii), &enterEvent))
1828 #endif // QT_NO_DRAGANDDROP
1830 #ifndef QT_NO_CURSOR
1831 void QQuickWindowPrivate::updateCursor(const QPointF &scenePos)
1835 QQuickItem *oldCursorItem = cursorItem;
1836 cursorItem = findCursorItem(rootItem, scenePos);
1838 if (cursorItem != oldCursorItem) {
1840 q->setCursor(cursorItem->cursor());
1846 QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF &scenePos)
1848 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1849 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1850 QPointF p = item->mapFromScene(scenePos);
1851 if (!item->contains(p))
1855 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1856 for (int ii = children.count() - 1; ii >= 0; --ii) {
1857 QQuickItem *child = children.at(ii);
1858 if (!child->isVisible() || !child->isEnabled())
1860 if (QQuickItem *cursorItem = findCursorItem(child, scenePos))
1864 if (itemPrivate->hasCursor) {
1865 QPointF p = item->mapFromScene(scenePos);
1866 if (item->contains(p))
1873 bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event)
1878 QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target);
1879 if (targetPrivate->filtersChildMouseEvents) {
1880 QScopedPointer<QTouchEvent> targetEvent(touchEventForItemBounds(target, *event));
1881 if (!targetEvent->touchPoints().isEmpty()) {
1882 QVector<int> touchIds;
1883 for (int i = 0; i < event->touchPoints().size(); ++i)
1884 touchIds.append(event->touchPoints().at(i).id());
1885 if (target->childMouseEventFilter(item, targetEvent.data())) {
1886 target->grabTouchPoints(touchIds);
1887 if (mouseGrabberItem) {
1888 mouseGrabberItem->ungrabMouse();
1894 // Only offer a mouse event to the filter if we have one point
1895 if (targetEvent->touchPoints().count() == 1) {
1897 const QTouchEvent::TouchPoint &tp = targetEvent->touchPoints().first();
1898 switch (tp.state()) {
1899 case Qt::TouchPointPressed:
1900 t = QEvent::MouseButtonPress;
1902 case Qt::TouchPointReleased:
1903 t = QEvent::MouseButtonRelease;
1906 // move or stationary
1907 t = QEvent::MouseMove;
1911 // targetEvent is already transformed wrt local position, velocity, etc.
1912 QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, targetEvent->touchPoints().first(), event, item, false));
1913 if (target->childMouseEventFilter(item, mouseEvent.data())) {
1914 itemForTouchPointId[tp.id()] = target;
1915 touchMouseId = tp.id();
1916 target->grabMouse();
1923 return sendFilteredTouchEvent(target->parentItem(), item, event);
1926 bool QQuickWindowPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QEvent *event)
1931 QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target);
1932 if (targetPrivate->filtersChildMouseEvents)
1933 if (target->childMouseEventFilter(item, event))
1936 if (sendFilteredMouseEvent(target->parentItem(), item, event))
1942 bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event)
1944 QStyleHints *styleHints = qApp->styleHints();
1945 int caps = QGuiApplicationPrivate::mouseEventCaps(event);
1946 bool dragVelocityLimitAvailable = (caps & QTouchDevice::Velocity)
1947 && styleHints->startDragVelocity();
1948 bool overThreshold = qAbs(d) > styleHints->startDragDistance();
1949 if (dragVelocityLimitAvailable) {
1950 QVector2D velocityVec = QGuiApplicationPrivate::mouseEventVelocity(event);
1951 qreal velocity = axis == Qt::XAxis ? velocityVec.x() : velocityVec.y();
1952 overThreshold |= qAbs(velocity) > styleHints->startDragVelocity();
1954 return overThreshold;
1958 Propagates an event to a QQuickItem on the window
1960 bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e)
1965 qWarning("QQuickWindow::sendEvent: Cannot send event to a null item");
1971 switch (e->type()) {
1972 case QEvent::KeyPress:
1973 case QEvent::KeyRelease:
1975 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1976 while (!e->isAccepted() && (item = item->parentItem())) {
1978 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1981 case QEvent::FocusIn:
1982 case QEvent::FocusOut:
1983 QQuickItemPrivate::get(item)->deliverFocusEvent(static_cast<QFocusEvent *>(e));
1985 case QEvent::MouseButtonPress:
1986 case QEvent::MouseButtonRelease:
1987 case QEvent::MouseButtonDblClick:
1988 case QEvent::MouseMove:
1989 // XXX todo - should sendEvent be doing this? how does it relate to forwarded events?
1990 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
1991 // accept because qml items by default accept and have to explicitly opt out of accepting
1993 QQuickItemPrivate::get(item)->deliverMouseEvent(static_cast<QMouseEvent *>(e));
1996 case QEvent::UngrabMouse:
1997 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
1999 item->mouseUngrabEvent();
2003 QQuickItemPrivate::get(item)->deliverWheelEvent(static_cast<QWheelEvent *>(e));
2005 case QEvent::HoverEnter:
2006 case QEvent::HoverLeave:
2007 case QEvent::HoverMove:
2008 QQuickItemPrivate::get(item)->deliverHoverEvent(static_cast<QHoverEvent *>(e));
2010 case QEvent::TouchBegin:
2011 case QEvent::TouchUpdate:
2012 case QEvent::TouchEnd:
2013 d->sendFilteredTouchEvent(item->parentItem(), item, static_cast<QTouchEvent *>(e));
2015 case QEvent::TouchCancel:
2016 QQuickItemPrivate::get(item)->deliverTouchEvent(static_cast<QTouchEvent *>(e));
2018 #ifndef QT_NO_DRAGANDDROP
2019 case QEvent::DragEnter:
2020 case QEvent::DragMove:
2021 case QEvent::DragLeave:
2023 QQuickItemPrivate::get(item)->deliverDragEvent(e);
2033 void QQuickWindowPrivate::cleanupNodes()
2035 for (int ii = 0; ii < cleanupNodeList.count(); ++ii)
2036 delete cleanupNodeList.at(ii);
2037 cleanupNodeList.clear();
2040 void QQuickWindowPrivate::cleanupNodesOnShutdown(QQuickItem *item)
2042 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
2043 if (p->itemNodeInstance) {
2044 delete p->itemNodeInstance;
2045 p->itemNodeInstance = 0;
2047 if (p->extra.isAllocated()) {
2048 p->extra->opacityNode = 0;
2049 p->extra->clipNode = 0;
2050 p->extra->rootNode = 0;
2057 for (int ii = 0; ii < p->childItems.count(); ++ii)
2058 cleanupNodesOnShutdown(p->childItems.at(ii));
2061 // This must be called from the render thread, with the main thread frozen
2062 void QQuickWindowPrivate::cleanupNodesOnShutdown()
2066 cleanupNodesOnShutdown(rootItem);
2067 QSet<QQuickItem *>::const_iterator it = parentlessItems.begin();
2068 for (; it != parentlessItems.end(); ++it)
2069 cleanupNodesOnShutdown(*it);
2070 q->cleanupSceneGraph();
2073 void QQuickWindowPrivate::updateDirtyNodes()
2076 qWarning() << "QQuickWindowPrivate::updateDirtyNodes():";
2081 QQuickItem *updateList = dirtyItemList;
2083 if (updateList) QQuickItemPrivate::get(updateList)->prevDirtyItem = &updateList;
2085 while (updateList) {
2086 QQuickItem *item = updateList;
2087 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
2088 itemPriv->removeFromDirtyList();
2091 qWarning() << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString());
2093 updateDirtyNode(item);
2097 void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item)
2099 #ifdef QML_RUNTIME_TESTING
2100 bool didFlash = false;
2103 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
2104 quint32 dirty = itemPriv->dirtyAttributes;
2105 itemPriv->dirtyAttributes = 0;
2107 if ((dirty & QQuickItemPrivate::TransformUpdateMask) ||
2108 (dirty & QQuickItemPrivate::Size && itemPriv->origin() != QQuickItem::TopLeft &&
2109 (itemPriv->scale() != 1. || itemPriv->rotation() != 0.))) {
2113 if (itemPriv->x != 0. || itemPriv->y != 0.)
2114 matrix.translate(itemPriv->x, itemPriv->y);
2116 for (int ii = itemPriv->transforms.count() - 1; ii >= 0; --ii)
2117 itemPriv->transforms.at(ii)->applyTo(&matrix);
2119 if (itemPriv->scale() != 1. || itemPriv->rotation() != 0.) {
2120 QPointF origin = item->transformOriginPoint();
2121 matrix.translate(origin.x(), origin.y());
2122 if (itemPriv->scale() != 1.)
2123 matrix.scale(itemPriv->scale(), itemPriv->scale());
2124 if (itemPriv->rotation() != 0.)
2125 matrix.rotate(itemPriv->rotation(), 0, 0, 1);
2126 matrix.translate(-origin.x(), -origin.y());
2129 itemPriv->itemNode()->setMatrix(matrix);
2132 bool clipEffectivelyChanged = (dirty & (QQuickItemPrivate::Clip | QQuickItemPrivate::Window)) &&
2133 ((item->clip() == false) != (itemPriv->clipNode() == 0));
2134 int effectRefCount = itemPriv->extra.isAllocated()?itemPriv->extra->effectRefCount:0;
2135 bool effectRefEffectivelyChanged = (dirty & (QQuickItemPrivate::EffectReference | QQuickItemPrivate::Window)) &&
2136 ((effectRefCount == 0) != (itemPriv->rootNode() == 0));
2138 if (clipEffectivelyChanged) {
2139 QSGNode *parent = itemPriv->opacityNode() ? (QSGNode *) itemPriv->opacityNode() :
2140 (QSGNode *)itemPriv->itemNode();
2141 QSGNode *child = itemPriv->rootNode() ? (QSGNode *)itemPriv->rootNode() :
2142 (QSGNode *)itemPriv->groupNode;
2145 Q_ASSERT(itemPriv->clipNode() == 0);
2146 itemPriv->extra.value().clipNode = new QQuickDefaultClipNode(item->clipRect());
2147 itemPriv->clipNode()->update();
2150 parent->removeChildNode(child);
2151 parent->appendChildNode(itemPriv->clipNode());
2153 itemPriv->clipNode()->appendChildNode(child);
2156 Q_ASSERT(itemPriv->clipNode() != 0);
2157 parent->removeChildNode(itemPriv->clipNode());
2159 itemPriv->clipNode()->removeChildNode(child);
2160 delete itemPriv->clipNode();
2161 itemPriv->extra->clipNode = 0;
2163 parent->appendChildNode(child);
2167 if (dirty & QQuickItemPrivate::ChildrenUpdateMask)
2168 itemPriv->childContainerNode()->removeAllChildNodes();
2170 if (effectRefEffectivelyChanged) {
2171 QSGNode *parent = itemPriv->clipNode();
2173 parent = itemPriv->opacityNode();
2175 parent = itemPriv->itemNode();
2176 QSGNode *child = itemPriv->groupNode;
2178 if (itemPriv->extra.isAllocated() && itemPriv->extra->effectRefCount) {
2179 Q_ASSERT(itemPriv->rootNode() == 0);
2180 itemPriv->extra->rootNode = new QSGRootNode;
2183 parent->removeChildNode(child);
2184 parent->appendChildNode(itemPriv->rootNode());
2186 itemPriv->rootNode()->appendChildNode(child);
2188 Q_ASSERT(itemPriv->rootNode() != 0);
2189 parent->removeChildNode(itemPriv->rootNode());
2191 itemPriv->rootNode()->removeChildNode(child);
2192 delete itemPriv->rootNode();
2193 itemPriv->extra->rootNode = 0;
2195 parent->appendChildNode(child);
2199 if (dirty & QQuickItemPrivate::ChildrenUpdateMask) {
2200 QSGNode *groupNode = itemPriv->groupNode;
2202 groupNode->removeAllChildNodes();
2204 QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems();
2207 for (; ii < orderedChildren.count() && orderedChildren.at(ii)->z() < 0; ++ii) {
2208 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
2209 if (!childPrivate->explicitVisible &&
2210 (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount))
2212 if (childPrivate->itemNode()->parent())
2213 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
2215 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
2218 QSGNode *beforePaintNode = itemPriv->groupNode ? itemPriv->groupNode->lastChild() : 0;
2219 if (beforePaintNode || itemPriv->extra.isAllocated())
2220 itemPriv->extra.value().beforePaintNode = beforePaintNode;
2222 if (itemPriv->paintNode)
2223 itemPriv->childContainerNode()->appendChildNode(itemPriv->paintNode);
2225 for (; ii < orderedChildren.count(); ++ii) {
2226 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
2227 if (!childPrivate->explicitVisible &&
2228 (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount))
2230 if (childPrivate->itemNode()->parent())
2231 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
2233 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
2237 if ((dirty & QQuickItemPrivate::Size) && itemPriv->clipNode()) {
2238 itemPriv->clipNode()->setRect(item->clipRect());
2239 itemPriv->clipNode()->update();
2242 if (dirty & (QQuickItemPrivate::OpacityValue | QQuickItemPrivate::Visible
2243 | QQuickItemPrivate::HideReference | QQuickItemPrivate::Window))
2245 qreal opacity = itemPriv->explicitVisible && (!itemPriv->extra.isAllocated() || itemPriv->extra->hideRefCount == 0)
2246 ? itemPriv->opacity() : qreal(0);
2248 if (opacity != 1 && !itemPriv->opacityNode()) {
2249 itemPriv->extra.value().opacityNode = new QSGOpacityNode;
2251 QSGNode *parent = itemPriv->itemNode();
2252 QSGNode *child = itemPriv->clipNode();
2254 child = itemPriv->rootNode();
2256 child = itemPriv->groupNode;
2259 parent->removeChildNode(child);
2260 parent->appendChildNode(itemPriv->opacityNode());
2262 itemPriv->opacityNode()->appendChildNode(child);
2264 if (itemPriv->opacityNode())
2265 itemPriv->opacityNode()->setOpacity(opacity);
2268 if (dirty & QQuickItemPrivate::ContentUpdateMask) {
2270 if (itemPriv->flags & QQuickItem::ItemHasContents) {
2271 updatePaintNodeData.transformNode = itemPriv->itemNode();
2272 itemPriv->paintNode = item->updatePaintNode(itemPriv->paintNode, &updatePaintNodeData);
2274 Q_ASSERT(itemPriv->paintNode == 0 ||
2275 itemPriv->paintNode->parent() == 0 ||
2276 itemPriv->paintNode->parent() == itemPriv->childContainerNode());
2278 if (itemPriv->paintNode && itemPriv->paintNode->parent() == 0) {
2279 if (itemPriv->extra.isAllocated() && itemPriv->extra->beforePaintNode)
2280 itemPriv->childContainerNode()->insertChildNodeAfter(itemPriv->paintNode, itemPriv->extra->beforePaintNode);
2282 itemPriv->childContainerNode()->prependChildNode(itemPriv->paintNode);
2284 } else if (itemPriv->paintNode) {
2285 delete itemPriv->paintNode;
2286 itemPriv->paintNode = 0;
2291 // Check consistency.
2292 const QSGNode *nodeChain[] = {
2293 itemPriv->itemNodeInstance,
2294 itemPriv->opacityNode(),
2295 itemPriv->clipNode(),
2296 itemPriv->rootNode(),
2297 itemPriv->groupNode,
2298 itemPriv->paintNode,
2303 while (ip < 5 && nodeChain[ip] == 0)
2308 while (ic < 5 && nodeChain[ic] == 0)
2310 const QSGNode *parent = nodeChain[ip];
2311 const QSGNode *child = nodeChain[ic];
2313 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 0);
2315 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 1);
2316 Q_ASSERT(child->parent() == parent);
2317 bool containsChild = false;
2318 for (QSGNode *n = parent->firstChild(); n; n = n->nextSibling())
2319 containsChild |= (n == child);
2320 Q_ASSERT(containsChild);
2326 #ifdef QML_RUNTIME_TESTING
2327 if (itemPriv->sceneGraphContext()->isFlashModeEnabled()) {
2328 QSGFlashNode *flash = new QSGFlashNode();
2329 flash->setRect(item->boundingRect());
2330 itemPriv->childContainerNode()->appendChildNode(flash);
2341 void QQuickWindow::maybeUpdate()
2344 d->windowManager->maybeUpdate(this);
2347 void QQuickWindow::cleanupSceneGraph()
2354 delete d->renderer->rootNode();
2361 Returns the opengl context used for rendering.
2363 If the scene graph is not ready, this function will return 0.
2365 \sa sceneGraphInitialized(), sceneGraphInvalidated()
2368 QOpenGLContext *QQuickWindow::openglContext() const
2370 Q_D(const QQuickWindow);
2371 if (d->context->isReady())
2372 return d->context->glContext();
2378 \fn void QSGContext::sceneGraphInitialized()
2380 This signal is emitted when the scene graph has been initialized.
2382 This signal will be emitted from the scene graph rendering thread.
2388 \fn void QSGContext::sceneGraphInvalidated()
2390 This signal is emitted when the scene graph has been invalidated.
2392 This signal implies that the opengl rendering context used
2393 has been invalidated and all user resources tied to that context
2396 This signal will be emitted from the scene graph rendering thread.
2401 Sets the render target for this window to be \a fbo.
2403 The specified fbo must be created in the context of the window
2404 or one that shares with it.
2407 This function can only be called from the thread doing
2411 void QQuickWindow::setRenderTarget(QOpenGLFramebufferObject *fbo)
2414 if (d->context && d->context && QThread::currentThread() != d->context->thread()) {
2415 qWarning("QQuickWindow::setRenderThread: Cannot set render target from outside the rendering thread");
2419 d->renderTarget = fbo;
2421 d->renderTargetId = fbo->handle();
2422 d->renderTargetSize = fbo->size();
2424 d->renderTargetId = 0;
2425 d->renderTargetSize = QSize();
2432 void QQuickWindow::setRenderTarget(uint fboId, const QSize &size)
2435 if (d->context && d->context && QThread::currentThread() != d->context->thread()) {
2436 qWarning("QQuickWindow::setRenderThread: Cannot set render target from outside the rendering thread");
2440 d->renderTargetId = fboId;
2441 d->renderTargetSize = size;
2443 // Unset any previously set instance...
2444 d->renderTarget = 0;
2449 Returns the FBO id of the render target when set; otherwise returns 0.
2451 uint QQuickWindow::renderTargetId() const
2453 Q_D(const QQuickWindow);
2454 return d->renderTargetId;
2458 Returns the size of the currently set render target; otherwise returns an enpty size.
2460 QSize QQuickWindow::renderTargetSize() const
2462 Q_D(const QQuickWindow);
2463 return d->renderTargetSize;
2470 Returns the render target for this window.
2472 The default is to render to the surface of the window, in which
2473 case the render target is 0.
2475 QOpenGLFramebufferObject *QQuickWindow::renderTarget() const
2477 Q_D(const QQuickWindow);
2478 return d->renderTarget;
2483 Grabs the contents of the window and returns it as an image.
2485 This function might not work if the window is not visible.
2487 \warning Calling this function will cause performance problems.
2489 \warning This function can only be called from the GUI thread.
2491 QImage QQuickWindow::grabWindow()
2494 return d->windowManager->grab(this);
2498 Returns an incubation controller that splices incubation between frames
2499 for this window. QQuickView automatically installs this controller for you,
2500 otherwise you will need to install it yourself using \l{QQmlEngine::setIncubationController()}.
2502 The controller is owned by the window and will be destroyed when the window
2505 QQmlIncubationController *QQuickWindow::incubationController() const
2507 Q_D(const QQuickWindow);
2509 if (!d->incubationController)
2510 d->incubationController = new QQuickWindowIncubationController(const_cast<QQuickWindowPrivate *>(d));
2511 return d->incubationController;
2517 \enum QQuickWindow::CreateTextureOption
2519 The CreateTextureOption enums are used to customize a texture is wrapped.
2521 \value TextureHasAlphaChannel The texture has an alpha channel and should
2522 be drawn using blending.
2524 \value TextureHasMipmaps The texture has mipmaps and can be drawn with
2527 \value TextureOwnsGLTexture The texture object owns the texture id and
2528 will delete the GL texture when the texture object is deleted.
2532 \fn void QQuickWindow::beforeRendering()
2534 This signal is emitted before the scene starts rendering.
2536 Combined with the modes for clearing the background, this option
2537 can be used to paint using raw GL under QML content.
2539 The GL context used for rendering the scene graph will be bound
2542 \warning Since this signal is emitted from the scene graph rendering thread, the
2543 receiver should be on the scene graph thread or the connection should be Qt::DirectConnection.
2545 \warning Make very sure that a signal handler for beforeRendering leaves the GL
2546 context in the same state as it was when the signal handler was entered. Failing to
2547 do so can result in the scene not rendering properly.
2551 \fn void QQuickWindow::afterRendering()
2553 This signal is emitted after the scene has completed rendering, before swapbuffers is called.
2555 This signal can be used to paint using raw GL on top of QML content,
2556 or to do screen scraping of the current frame buffer.
2558 The GL context used for rendering the scene graph will be bound at this point.
2560 \warning Since this signal is emitted from the scene graph rendering thread, the
2561 receiver should be on the scene graph thread or the connection should be Qt::DirectConnection.
2563 \warning Make very sure that a signal handler for afterRendering() leaves the GL
2564 context in the same state as it was when the signal handler was entered. Failing to
2565 do so can result in the scene not rendering properly.
2571 Sets weither the scene graph rendering of QML should clear the color buffer
2572 before it starts rendering to \a enbled.
2574 By disabling clearing of the color buffer, it is possible to do GL painting
2575 under the scene graph.
2577 The color buffer is cleared by default.
2579 \sa beforeRendering()
2582 void QQuickWindow::setClearBeforeRendering(bool enabled)
2585 d->clearBeforeRendering = enabled;
2591 Returns weither clearing of the color buffer is done before rendering or not.
2594 bool QQuickWindow::clearBeforeRendering() const
2596 Q_D(const QQuickWindow);
2597 return d->clearBeforeRendering;
2603 Creates a new QSGTexture from the supplied \a image. If the image has an
2604 alpha channel, the corresponding texture will have an alpha channel.
2606 The caller of the function is responsible for deleting the returned texture.
2607 The actual GL texture will be deleted when the texture object is deleted.
2609 Depending on the underlying implementation of the scene graph, the returned
2610 texture may be part of an atlas. For code to be portable across implementations
2611 one should always use the texture coordinates returned from
2612 QSGTexture::normalizedTextureSubRect() when building geometry.
2614 \warning This function will return 0 if the scene graph has not yet been
2617 \warning The returned texture is not memory managed by the scene graph and
2618 must be explicitely deleted by the caller on the rendering thread.
2619 This is acheived by deleting the texture from a QSGNode destructor
2620 or by using deleteLater() in the case where the texture already has affinity
2621 to the rendering thread.
2623 This function can be called from any thread.
2625 \sa sceneGraphInitialized()
2628 QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const
2630 Q_D(const QQuickWindow);
2631 if (d->context && d->context->isReady())
2632 return d->context->createTexture(image);
2640 Creates a new QSGTexture object from an existing GL texture \a id.
2642 The caller of the function is responsible for deleting the returned texture.
2644 Use \a options to customize the texture attributes.
2646 \warning This function will return 0 if the scenegraph has not yet been
2649 \sa sceneGraphInitialized()
2651 QSGTexture *QQuickWindow::createTextureFromId(uint id, const QSize &size, CreateTextureOptions options) const
2653 Q_D(const QQuickWindow);
2654 if (d->context && d->context->isReady()) {
2655 QSGPlainTexture *texture = new QSGPlainTexture();
2656 texture->setTextureId(id);
2657 texture->setHasAlphaChannel(options & TextureHasAlphaChannel);
2658 texture->setHasMipmaps(options & TextureHasMipmaps);
2659 texture->setOwnsTexture(options & TextureOwnsGLTexture);
2660 texture->setTextureSize(size);
2668 Sets the color used to clear the opengl context to \a color.
2670 Setting the clear color has no effect when clearing is disabled.
2672 \sa setClearBeforeRendering()
2675 void QQuickWindow::setColor(const QColor &color)
2678 if (color == d->clearColor)
2681 d->clearColor = color;
2682 emit colorChanged(color);
2688 Returns the color used to clear the opengl context.
2691 QColor QQuickWindow::color() const
2693 return d_func()->clearColor;
2698 #include "moc_qquickwindow.cpp"