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());
84 class QQuickWindowIncubationController : public QObject, public QQmlIncubationController
89 QQuickWindowIncubationController(const QQuickWindow *window)
90 : m_window(QQuickWindowPrivate::get(const_cast<QQuickWindow *>(window)))
92 // Allow incubation for 1/3 of a frame.
93 m_incubation_time = qMax(1, int(1000 / QGuiApplication::primaryScreen()->refreshRate()) / 3);
95 m_animation_driver = m_window->windowManager->animationDriver();
96 if (m_animation_driver) {
97 connect(m_animation_driver, SIGNAL(stopped()), this, SLOT(animationStopped()));
98 connect(window, SIGNAL(frameSwapped()), this, SLOT(incubate()));
103 virtual bool event(QEvent *e)
105 if (e->type() == QEvent::User) {
109 return QObject::event(e);
114 if (incubatingObjectCount()) {
115 if (m_animation_driver && m_animation_driver->isRunning()) {
116 incubateFor(m_incubation_time);
118 incubateFor(m_incubation_time * 2);
119 if (incubatingObjectCount())
120 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
125 void animationStopped() { incubate(); }
128 virtual void incubatingObjectCountChanged(int count)
130 if (count && (!m_animation_driver || !m_animation_driver->isRunning()))
131 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
135 QQuickWindowPrivate *m_window;
136 int m_incubation_time;
137 QAnimationDriver *m_animation_driver;
140 #include "qquickwindow.moc"
143 #ifndef QT_NO_ACCESSIBILITY
145 Returns an accessibility interface for this window, or 0 if such an
146 interface cannot be created.
148 \warning The caller is responsible for deleting the returned interface.
150 QAccessibleInterface *QQuickWindow::accessibleRoot() const
152 return QAccessible::queryAccessibleInterface(const_cast<QQuickWindow*>(this));
161 Prior to being added to a valid window items can set and clear focus with no
162 effect. Only once items are added to a window (by way of having a parent set that
163 already belongs to a window) do the focus rules apply. Focus goes back to
164 having no effect if an item is removed from a window.
166 When an item is moved into a new focus scope (either being added to a window
167 for the first time, or having its parent changed), if the focus scope already has
168 a scope focused item that takes precedence over the item being added. Otherwise,
169 the focus of the added tree is used. In the case of of a tree of items being
170 added to a window for the first time, which may have a conflicted focus state (two
171 or more items in one scope having focus set), the same rule is applied item by item -
172 thus the first item that has focus will get it (assuming the scope doesn't already
173 have a scope focused item), and the other items will have their focus cleared.
177 // #define FOCUS_DEBUG
178 // #define MOUSE_DEBUG
179 // #define TOUCH_DEBUG
180 // #define DIRTY_DEBUG
183 void printFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1);
186 QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData()
191 QQuickRootItem::QQuickRootItem()
196 void QQuickWindow::exposeEvent(QExposeEvent *)
199 d->windowManager->exposureChanged(this);
203 void QQuickWindow::resizeEvent(QResizeEvent *)
206 d->windowManager->resize(this, size());
210 void QQuickWindow::showEvent(QShowEvent *)
212 d_func()->windowManager->show(this);
216 void QQuickWindow::hideEvent(QHideEvent *)
218 d_func()->windowManager->hide(this);
222 void QQuickWindow::focusOutEvent(QFocusEvent *)
225 d->rootItem->setFocus(false);
229 void QQuickWindow::focusInEvent(QFocusEvent *)
232 d->rootItem->setFocus(true);
233 d->updateFocusItemTransform();
237 void QQuickWindowPrivate::polishItems()
239 int maxPolishCycles = 100000;
241 while (!itemsToPolish.isEmpty() && --maxPolishCycles > 0) {
242 QSet<QQuickItem *> itms = itemsToPolish;
243 itemsToPolish.clear();
245 for (QSet<QQuickItem *>::iterator it = itms.begin(); it != itms.end(); ++it) {
246 QQuickItem *item = *it;
247 QQuickItemPrivate::get(item)->polishScheduled = false;
248 item->updatePolish();
252 if (maxPolishCycles == 0)
253 qWarning("QQuickWindow: possible QQuickItem::polish() loop");
255 updateFocusItemTransform();
259 * This parameter enables that this window can be rendered without
260 * being shown on screen. This feature is very limited in what it supports.
262 * There needs to be another window actually showing that we can make current
263 * to get a surface to make current AND for this feature to be useful
264 * one needs to hook into beforeRender() and set the render tareget.
267 void QQuickWindowPrivate::setRenderWithoutShowing(bool render)
269 if (render == renderWithoutShowing)
273 renderWithoutShowing = render;
276 windowManager->show(q);
278 windowManager->hide(q);
283 * Schedules the window to render another frame.
285 * Calling QQuickWindow::update() differs from QQuickItem::update() in that
286 * it always triggers a repaint, regardless of changes in the underlying
287 * scene graph or not.
289 void QQuickWindow::update()
292 d->windowManager->update(this);
295 void forceUpdate(QQuickItem *item)
297 if (item->flags() & QQuickItem::ItemHasContents)
299 QQuickItemPrivate::get(item)->dirty(QQuickItemPrivate::ChildrenUpdateMask);
301 QList <QQuickItem *> items = item->childItems();
302 for (int i=0; i<items.size(); ++i)
303 forceUpdate(items.at(i));
306 void QQuickWindowPrivate::syncSceneGraph()
308 QML_MEMORY_SCOPE_STRING("SceneGraph");
311 emit q->beforeSynchronizing();
313 forceUpdate(rootItem);
315 QSGRootNode *rootNode = new QSGRootNode;
316 rootNode->appendChildNode(QQuickItemPrivate::get(rootItem)->itemNode());
317 renderer = context->createRenderer();
318 renderer->setRootNode(rootNode);
323 // Copy the current state of clearing from window into renderer.
324 renderer->setClearColor(clearColor);
325 QSGRenderer::ClearMode mode = QSGRenderer::ClearStencilBuffer | QSGRenderer::ClearDepthBuffer;
326 if (clearBeforeRendering)
327 mode |= QSGRenderer::ClearColorBuffer;
328 renderer->setClearMode(mode);
332 void QQuickWindowPrivate::renderSceneGraph(const QSize &size)
334 QML_MEMORY_SCOPE_STRING("SceneGraph");
336 emit q->beforeRendering();
338 renderer->setDeviceRect(QRect(QPoint(0, 0), size));
339 if (renderTargetId) {
340 fboId = renderTargetId;
341 renderer->setViewportRect(QRect(QPoint(0, 0), renderTargetSize));
343 renderer->setViewportRect(QRect(QPoint(0, 0), size));
345 renderer->setProjectionMatrixToDeviceRect();
347 context->renderNextFrame(renderer, fboId);
348 emit q->afterRendering();
351 QQuickWindowPrivate::QQuickWindowPrivate()
354 , mouseGrabberItem(0)
359 , touchMousePressTimestamp(0)
360 , renderWithoutShowing(false)
365 , clearColor(Qt::white)
366 , clearBeforeRendering(true)
367 , persistentGLContext(false)
368 , persistentSceneGraph(false)
369 , lastWheelEventAccepted(false)
372 , incubationController(0)
376 QQuickWindowPrivate::~QQuickWindowPrivate()
380 void QQuickWindowPrivate::init(QQuickWindow *c)
386 rootItem = new QQuickRootItem;
387 QQmlEngine::setObjectOwnership(rootItem, QQmlEngine::CppOwnership);
388 QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(rootItem);
389 rootItemPrivate->window = q;
390 rootItemPrivate->windowRefCount = 1;
391 rootItemPrivate->flags |= QQuickItem::ItemIsFocusScope;
393 // In the absence of a focus in event on some platforms assume the window will
394 // be activated immediately and set focus on the rootItem
395 // ### Remove when QTBUG-22415 is resolved.
396 //It is important that this call happens after the rootItem has a window..
397 rootItem->setFocus(true);
399 windowManager = QQuickWindowManager::instance();
400 context = windowManager->sceneGraphContext();
401 q->setSurfaceType(QWindow::OpenGLSurface);
402 q->setFormat(context->defaultSurfaceFormat());
404 QObject::connect(context, SIGNAL(initialized()), q, SIGNAL(sceneGraphInitialized()), Qt::DirectConnection);
405 QObject::connect(context, SIGNAL(invalidated()), q, SIGNAL(sceneGraphInvalidated()), Qt::DirectConnection);
406 QObject::connect(context, SIGNAL(invalidated()), q, SLOT(cleanupSceneGraph()), Qt::DirectConnection);
410 \property QQuickWindow::data
414 QQmlListProperty<QObject> QQuickWindowPrivate::data()
417 return QQuickItemPrivate::get(rootItem)->data();
420 void QQuickWindowPrivate::initRootItem()
423 q->connect(q, SIGNAL(widthChanged(int)),
424 rootItem, SLOT(setWidth(int)));
425 q->connect(q, SIGNAL(heightChanged(int)),
426 rootItem, SLOT(setHeight(int)));
427 rootItem->setWidth(q->width());
428 rootItem->setHeight(q->height());
431 static QMouseEvent *touchToMouseEvent(QEvent::Type type, const QTouchEvent::TouchPoint &p, QTouchEvent *event, QQuickItem *item, bool transformNeeded = true)
433 // The touch point local position and velocity are not yet transformed.
434 QMouseEvent *me = new QMouseEvent(type, transformNeeded ? item->mapFromScene(p.scenePos()) : p.pos(), p.scenePos(), p.screenPos(),
435 Qt::LeftButton, Qt::LeftButton, event->modifiers());
436 me->setAccepted(true);
437 me->setTimestamp(event->timestamp());
438 QVector2D transformedVelocity = p.velocity();
439 if (transformNeeded) {
440 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
441 QMatrix4x4 transformMatrix(itemPrivate->windowToItemTransform());
442 transformedVelocity = transformMatrix.mapVector(p.velocity()).toVector2D();
444 QGuiApplicationPrivate::setMouseEventCapsAndVelocity(me, event->device()->capabilities(), transformedVelocity);
448 bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *event)
451 // For each point, check if it is accepted, if not, try the next point.
452 // Any of the fingers can become the mouse one.
453 // This can happen because a mouse area might not accept an event at some point but another.
454 for (int i = 0; i < event->touchPoints().count(); ++i) {
455 const QTouchEvent::TouchPoint &p = event->touchPoints().at(i);
457 if (touchMouseId == -1 && p.state() & Qt::TouchPointPressed) {
458 QPointF pos = item->mapFromScene(p.scenePos());
460 // probably redundant, we check bounds in the calling function (matchingNewPoints)
461 if (!item->contains(pos))
464 bool doubleClick = event->timestamp() - touchMousePressTimestamp
465 < static_cast<ulong>(qApp->styleHints()->mouseDoubleClickInterval());
466 touchMousePressTimestamp = event->timestamp();
467 // Store the id already here and restore it to -1 if the event does not get
468 // accepted. Cannot defer setting the new value because otherwise if the event
469 // handler spins the event loop all subsequent moves and releases get lost.
470 touchMouseId = p.id();
471 itemForTouchPointId[touchMouseId] = item;
472 QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event, item));
474 // Send a single press and see if that's accepted
475 if (!mouseGrabberItem)
477 item->grabTouchPoints(QVector<int>() << touchMouseId);
479 q->sendEvent(item, mousePress.data());
480 event->setAccepted(mousePress->isAccepted());
481 if (!mousePress->isAccepted()) {
483 if (itemForTouchPointId.value(p.id()) == item)
484 itemForTouchPointId.remove(p.id());
486 if (mouseGrabberItem == item)
490 if (doubleClick && mousePress->isAccepted()) {
491 touchMousePressTimestamp = 0;
492 QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item));
493 q->sendEvent(item, mouseDoubleClick.data());
494 event->setAccepted(mouseDoubleClick->isAccepted());
495 if (mouseDoubleClick->isAccepted()) {
501 // The event was accepted, we are done.
502 if (mousePress->isAccepted())
504 // The event was not accepted but touchMouseId was set.
505 if (touchMouseId != -1)
507 // try the next point
509 // Touch point was there before and moved
510 } else if (p.id() == touchMouseId) {
511 if (p.state() & Qt::TouchPointMoved) {
512 if (mouseGrabberItem) {
513 QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, mouseGrabberItem));
514 q->sendEvent(mouseGrabberItem, me.data());
515 event->setAccepted(me->isAccepted());
516 if (me->isAccepted()) {
517 itemForTouchPointId[p.id()] = mouseGrabberItem; // N.B. the mouseGrabberItem may be different after returning from sendEvent()
521 // no grabber, check if we care about mouse hover
522 // FIXME: this should only happen once, not recursively... I'll ignore it just ignore hover now.
523 // hover for touch???
524 QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, item));
525 if (lastMousePosition.isNull())
526 lastMousePosition = me->windowPos();
527 QPointF last = lastMousePosition;
528 lastMousePosition = me->windowPos();
530 bool accepted = me->isAccepted();
531 bool delivered = deliverHoverEvent(rootItem, me->windowPos(), last, me->modifiers(), accepted);
533 //take care of any exits
534 accepted = clearHover();
536 me->setAccepted(accepted);
539 } else if (p.state() & Qt::TouchPointReleased) {
540 // currently handled point was released
542 if (mouseGrabberItem) {
543 QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseButtonRelease, p, event, mouseGrabberItem));
544 q->sendEvent(mouseGrabberItem, me.data());
545 if (mouseGrabberItem) // might have ungrabbed due to event
546 mouseGrabberItem->ungrabMouse();
547 return me->isAccepted();
556 void QQuickWindowPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform)
558 QMatrix4x4 transformMatrix(transform);
559 for (int i=0; i<touchPoints.count(); i++) {
560 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
561 touchPoint.setRect(transform.mapRect(touchPoint.sceneRect()));
562 touchPoint.setStartPos(transform.map(touchPoint.startScenePos()));
563 touchPoint.setLastPos(transform.map(touchPoint.lastScenePos()));
564 touchPoint.setVelocity(transformMatrix.mapVector(touchPoint.velocity()).toVector2D());
570 Translates the data in \a touchEvent to this window. This method leaves the item local positions in
571 \a touchEvent untouched (these are filled in later).
573 void QQuickWindowPrivate::translateTouchEvent(QTouchEvent *touchEvent)
575 QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
576 for (int i = 0; i < touchPoints.count(); ++i) {
577 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
579 touchPoint.setScreenRect(touchPoint.sceneRect());
580 touchPoint.setStartScreenPos(touchPoint.startScenePos());
581 touchPoint.setLastScreenPos(touchPoint.lastScenePos());
583 touchPoint.setSceneRect(touchPoint.rect());
584 touchPoint.setStartScenePos(touchPoint.startPos());
585 touchPoint.setLastScenePos(touchPoint.lastPos());
588 lastMousePosition = touchPoint.pos().toPoint();
590 touchEvent->setTouchPoints(touchPoints);
593 void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
598 Q_ASSERT(scope || item == rootItem);
601 qWarning() << "QQuickWindowPrivate::setFocusInScope():";
602 qWarning() << " scope:" << (QObject *)scope;
604 qWarning() << " scopeSubFocusItem:" << (QObject *)QQuickItemPrivate::get(scope)->subFocusItem;
605 qWarning() << " item:" << (QObject *)item;
606 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
609 QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0;
610 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
612 QQuickItem *oldActiveFocusItem = 0;
613 QQuickItem *newActiveFocusItem = 0;
615 QVarLengthArray<QQuickItem *, 20> changed;
617 // Does this change the active focus?
618 if (item == rootItem || (scopePrivate->activeFocus && item->isEnabled())) {
619 oldActiveFocusItem = activeFocusItem;
620 newActiveFocusItem = item;
621 while (newActiveFocusItem->isFocusScope()
622 && newActiveFocusItem->scopedFocusItem()
623 && newActiveFocusItem->scopedFocusItem()->isEnabled()) {
624 newActiveFocusItem = newActiveFocusItem->scopedFocusItem();
627 if (oldActiveFocusItem) {
629 qApp->inputMethod()->commit();
633 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
634 q->sendEvent(oldActiveFocusItem, &event);
636 QQuickItem *afi = oldActiveFocusItem;
637 while (afi && afi != scope) {
638 if (QQuickItemPrivate::get(afi)->activeFocus) {
639 QQuickItemPrivate::get(afi)->activeFocus = false;
642 afi = afi->parentItem();
647 if (item != rootItem && !(options & DontChangeSubFocusItem)) {
648 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
649 if (oldSubFocusItem) {
650 QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
651 changed << oldSubFocusItem;
654 QQuickItemPrivate::get(item)->updateSubFocusItem(scope, true);
657 if (!(options & DontChangeFocusProperty)) {
658 // if (item != rootItem || QGuiApplication::focusWindow() == q) { // QTBUG-22415
659 itemPrivate->focus = true;
664 if (newActiveFocusItem && rootItem->hasFocus()) {
665 activeFocusItem = newActiveFocusItem;
667 QQuickItemPrivate::get(newActiveFocusItem)->activeFocus = true;
668 changed << newActiveFocusItem;
670 QQuickItem *afi = newActiveFocusItem->parentItem();
671 while (afi && afi != scope) {
672 if (afi->isFocusScope()) {
673 QQuickItemPrivate::get(afi)->activeFocus = true;
676 afi = afi->parentItem();
679 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
680 q->sendEvent(newActiveFocusItem, &event);
683 emit q->focusObjectChanged(activeFocusItem);
685 if (!changed.isEmpty())
686 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
689 void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
694 Q_ASSERT(scope || item == rootItem);
697 qWarning() << "QQuickWindowPrivate::clearFocusInScope():";
698 qWarning() << " scope:" << (QObject *)scope;
699 qWarning() << " item:" << (QObject *)item;
700 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
703 QQuickItemPrivate *scopePrivate = 0;
705 scopePrivate = QQuickItemPrivate::get(scope);
706 if ( !scopePrivate->subFocusItem )
707 return;//No focus, nothing to do.
710 QQuickItem *oldActiveFocusItem = 0;
711 QQuickItem *newActiveFocusItem = 0;
713 QVarLengthArray<QQuickItem *, 20> changed;
715 Q_ASSERT(item == rootItem || item == scopePrivate->subFocusItem);
717 // Does this change the active focus?
718 if (item == rootItem || scopePrivate->activeFocus) {
719 oldActiveFocusItem = activeFocusItem;
720 newActiveFocusItem = scope;
722 Q_ASSERT(oldActiveFocusItem);
725 qApp->inputMethod()->commit();
729 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
730 q->sendEvent(oldActiveFocusItem, &event);
732 QQuickItem *afi = oldActiveFocusItem;
733 while (afi && afi != scope) {
734 if (QQuickItemPrivate::get(afi)->activeFocus) {
735 QQuickItemPrivate::get(afi)->activeFocus = false;
738 afi = afi->parentItem();
742 if (item != rootItem && !(options & DontChangeSubFocusItem)) {
743 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
744 if (oldSubFocusItem && !(options & DontChangeFocusProperty)) {
745 QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
746 changed << oldSubFocusItem;
749 QQuickItemPrivate::get(item)->updateSubFocusItem(scope, false);
751 } else if (!(options & DontChangeFocusProperty)) {
752 QQuickItemPrivate::get(item)->focus = false;
756 if (newActiveFocusItem) {
757 Q_ASSERT(newActiveFocusItem == scope);
758 activeFocusItem = scope;
760 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
761 q->sendEvent(newActiveFocusItem, &event);
764 emit q->focusObjectChanged(activeFocusItem);
766 if (!changed.isEmpty())
767 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
770 void QQuickWindowPrivate::notifyFocusChangesRecur(QQuickItem **items, int remaining)
772 QQmlGuard<QQuickItem> item(*items);
775 notifyFocusChangesRecur(items + 1, remaining - 1);
778 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
780 if (itemPrivate->notifiedFocus != itemPrivate->focus) {
781 itemPrivate->notifiedFocus = itemPrivate->focus;
782 emit item->focusChanged(itemPrivate->focus);
785 if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) {
786 itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus;
787 itemPrivate->itemChange(QQuickItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus);
788 emit item->activeFocusChanged(itemPrivate->activeFocus);
793 void QQuickWindowPrivate::dirtyItem(QQuickItem *)
799 void QQuickWindowPrivate::cleanup(QSGNode *n)
803 Q_ASSERT(!cleanupNodeList.contains(n));
804 cleanupNodeList.append(n);
810 \instantiates QQuickWindow
811 \inqmlmodule QtQuick.Window 2
812 \ingroup qtquick-visual
813 \brief Creates a new top-level window
815 The Window object creates a new top-level window for a QtQuick scene. It automatically sets up the
816 window for use with QtQuick 2.0 graphical types.
818 To use this type, you will need to import the module with the following line:
820 import QtQuick.Window 2.0
823 Restricting this import will allow you to have a QML environment without access to window system features.
831 \brief The QQuickWindow class provides the window for displaying a graphical QML scene
833 QQuickWindow provides the graphical scene management needed to interact with and display
834 a scene of QQuickItems.
836 A QQuickWindow always has a single invisible root item. To add items to this window,
837 reparent the items to the root item or to an existing item in the scene.
839 For easily displaying a scene from a QML file, see \l{QQuickView}.
841 \section1 Scene Graph and Rendering
843 The QQuickWindow uses a scene graph on top of OpenGL to render. This scene graph is disconnected
844 from the QML scene and potentially lives in another thread, depending on the platform
845 implementation. Since the rendering scene graph lives independently from the QML scene, it can
846 also be completely released without affecting the state of the QML scene.
848 The sceneGraphInitialized() signal is emitted on the rendering thread before the QML scene is
849 rendered to the screen for the first time. If the rendering scene graph has been released
850 the signal will be emitted again before the next frame is rendered.
852 The rendering of each frame is broken down into the following
853 steps, in the given order:
857 \li The QQuickWindow::beforeSynchronizing() signal is emitted.
858 Applications can make direct connections (Qt::DirectConnection)
859 to this signal to do any preparation required before calls to
860 QQuickItem::updatePaintNode().
862 \li Synchronization of the QML state into the scene graph. This is
863 done by calling the QQuickItem::updatePaintNode() function on all
864 items that have changed since the previous frame. When a dedicated
865 rendering thread is used, the GUI thread is blocked during this
866 synchroniation. This is the only time the QML items and the nodes
867 in the scene graph interact.
869 \li The window to be rendered is made current using
870 QOpenGLContext::makeCurrent().
872 \li The QQuickWindow::beforeRendering() signal is
873 emitted. Applications can make direct connections
874 (Qt::DirectConnection) to this signal to use custom OpenGL calls
875 which will then stack visually beneath the QML scene.
877 \li Items that have specified QSGNode::UsesPreprocess, will have their
878 QSGNode::preprocess() function invoked.
880 \li The QQuickWindow is cleared according to what is specified
881 using QQuickWindow::setClearBeforeRendering() and
882 QQuickWindow::setClearColor().
884 \li The scene graph is rendered.
886 \li The QQuickWindow::afterRendering() signal is
887 emitted. Applications can make direct connections
888 (Qt::DirectConnection) to this signal to use custom OpenGL calls
889 which will then stack visually over the QML scene.
891 \li The rendered frame is swapped and QQuickWindow::frameSwapped()
896 All of the above happen on the rendering thread, when applicable.
898 While the scene graph is being rendered on the rendering thread, the GUI will process animations
899 for the next frame. This means that as long as users are not using scene graph API
900 directly, the added complexity of a rendering thread can be completely ignored.
902 When a QQuickWindow is programatically hidden with hide() or setVisible(false), it will
903 stop rendering and its scene graph and OpenGL context might be released. The
904 sceneGraphInvalidated() signal will be emitted when this happens.
906 \warning It is crucial that OpenGL operations and interaction with the scene graph happens
907 exclusively on the rendering thread, primarily during the updatePaintNode() phase.
909 \warning As signals related to rendering might be emitted from the rendering thread,
910 connections should be made using Qt::DirectConnection
913 \section2 Resource Management
915 QML will typically try to cache images, scene graph nodes, etc to improve performance, but in
916 some low-memory scenarios it might be required to aggressively release these resources. The
917 releaseResources() can be used to force clean up of certain resources. Calling releaseResources()
918 may result in the entire scene graph and its OpenGL context being deleted. The
919 sceneGraphInvalidated() signal will be emitted when this happens.
921 \sa {OpenGL Under QML Example}
926 Constructs a window for displaying a QML scene with parent window \a parent.
928 QQuickWindow::QQuickWindow(QWindow *parent)
929 : QWindow(*(new QQuickWindowPrivate), parent)
938 QQuickWindow::QQuickWindow(QQuickWindowPrivate &dd, QWindow *parent)
939 : QWindow(dd, parent)
948 QQuickWindow::~QQuickWindow()
952 d->windowManager->windowDestroyed(this);
954 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
955 delete d->incubationController; d->incubationController = 0;
957 delete d->rootItem; d->rootItem = 0;
963 This function tries to release redundant resources currently held by the QML scene.
965 Calling this function might result in the scene graph and the OpenGL context used
966 for rendering being released to release graphics memory. If this happens, the
967 sceneGraphInvalidated() signal will be called, allowing users to clean up their
968 own graphics resources. The setPersistentOpenGLContext() and setPersistentSceneGraph()
969 functions can be used to prevent this from happening, if handling the cleanup is
970 not feasible in the application, at the cost of higher memory usage.
972 \sa sceneGraphInvalidated(), setPersistentOpenGLContext(), setPersistentSceneGraph()
975 void QQuickWindow::releaseResources()
978 d->windowManager->releaseResources();
979 QQuickPixmap::purgeCache();
985 Sets whether the OpenGL context can be released as a part of a call to
986 releaseResources() to \a persistent.
988 The OpenGL context might still be released when the user makes an explicit
991 \sa setPersistentSceneGraph()
994 void QQuickWindow::setPersistentOpenGLContext(bool persistent)
997 d->persistentGLContext = persistent;
1002 Returns whether the OpenGL context can be released as a part of a call to
1006 bool QQuickWindow::isPersistentOpenGLContext() const
1008 Q_D(const QQuickWindow);
1009 return d->persistentGLContext;
1015 Sets whether the scene graph nodes and resources can be released as a
1016 part of a call to releaseResources() to \a persistent.
1018 The scene graph nodes and resources might still be released when the user
1019 makes an explicit call to hide().
1021 \sa setPersistentOpenGLContext()
1024 void QQuickWindow::setPersistentSceneGraph(bool persistent)
1027 d->persistentSceneGraph = persistent;
1033 Returns whether the scene graph nodes and resources can be released as a part
1034 of a call to releaseResources().
1037 bool QQuickWindow::isPersistentSceneGraph() const
1039 Q_D(const QQuickWindow);
1040 return d->persistentSceneGraph;
1048 \property QQuickWindow::contentItem
1049 \brief The invisible root item of the scene.
1051 A QQuickWindow always has a single invisible root item containing all of its content.
1052 To add items to this window, reparent the items to the contentItem or to an existing
1055 QQuickItem *QQuickWindow::contentItem() const
1057 Q_D(const QQuickWindow);
1063 Returns the item which currently has active focus.
1065 QQuickItem *QQuickWindow::activeFocusItem() const
1067 Q_D(const QQuickWindow);
1069 return d->activeFocusItem;
1076 QObject *QQuickWindow::focusObject() const
1078 Q_D(const QQuickWindow);
1080 if (d->activeFocusItem)
1081 return d->activeFocusItem;
1082 return const_cast<QQuickWindow*>(this);
1087 Returns the item which currently has the mouse grab.
1089 QQuickItem *QQuickWindow::mouseGrabberItem() const
1091 Q_D(const QQuickWindow);
1093 return d->mouseGrabberItem;
1098 \qmlproperty color QtQuick.Window2::Window::color
1100 The background color for the window.
1102 Setting this property is more efficient than using a separate Rectangle.
1105 bool QQuickWindowPrivate::clearHover()
1108 if (hoverItems.isEmpty())
1111 QPointF pos = q->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint());
1113 bool accepted = false;
1114 foreach (QQuickItem* item, hoverItems)
1115 accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), true) || accepted;
1121 bool QQuickWindow::event(QEvent *e)
1125 switch (e->type()) {
1127 case QEvent::TouchBegin:
1128 case QEvent::TouchUpdate:
1129 case QEvent::TouchEnd: {
1130 QTouchEvent *touch = static_cast<QTouchEvent*>(e);
1131 d->translateTouchEvent(touch);
1132 // return in order to avoid the QWindow::event below
1133 return d->deliverTouchEvent(touch);
1136 case QEvent::TouchCancel:
1137 // return in order to avoid the QWindow::event below
1138 return d->deliverTouchCancelEvent(static_cast<QTouchEvent*>(e));
1142 d->lastMousePosition = QPoint();
1144 #ifndef QT_NO_DRAGANDDROP
1145 case QEvent::DragEnter:
1146 case QEvent::DragLeave:
1147 case QEvent::DragMove:
1149 d->deliverDragEvent(&d->dragGrabber, e);
1152 case QEvent::WindowDeactivate:
1153 rootItem()->windowDeactivateEvent();
1155 case QEvent::FocusAboutToChange:
1156 if (d->activeFocusItem)
1157 qGuiApp->inputMethod()->commit();
1163 return QWindow::event(e);
1167 void QQuickWindow::keyPressEvent(QKeyEvent *e)
1171 if (d->activeFocusItem)
1172 sendEvent(d->activeFocusItem, e);
1176 void QQuickWindow::keyReleaseEvent(QKeyEvent *e)
1180 if (d->activeFocusItem)
1181 sendEvent(d->activeFocusItem, e);
1184 QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos)
1186 int caps = QGuiApplicationPrivate::mouseEventCaps(event);
1187 QVector2D velocity = QGuiApplicationPrivate::mouseEventVelocity(event);
1188 QMouseEvent *me = new QMouseEvent(event->type(),
1189 transformedLocalPos ? *transformedLocalPos : event->localPos(),
1190 event->windowPos(), event->screenPos(),
1191 event->button(), event->buttons(), event->modifiers());
1192 QGuiApplicationPrivate::setMouseEventCapsAndVelocity(me, caps, velocity);
1193 me->setTimestamp(event->timestamp());
1197 bool QQuickWindowPrivate::deliverInitialMousePressEvent(QQuickItem *item, QMouseEvent *event)
1201 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1203 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1204 QPointF p = item->mapFromScene(event->windowPos());
1205 if (!item->contains(p))
1209 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1210 for (int ii = children.count() - 1; ii >= 0; --ii) {
1211 QQuickItem *child = children.at(ii);
1212 if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled)
1214 if (deliverInitialMousePressEvent(child, event))
1218 if (itemPrivate->acceptedMouseButtons() & event->button()) {
1219 QPointF localPos = item->mapFromScene(event->windowPos());
1220 if (item->contains(localPos)) {
1221 QScopedPointer<QMouseEvent> me(cloneMouseEvent(event, &localPos));
1224 q->sendEvent(item, me.data());
1225 event->setAccepted(me->isAccepted());
1226 if (me->isAccepted())
1228 if (mouseGrabberItem && !event->buttons())
1229 mouseGrabberItem->ungrabMouse();
1236 bool QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event)
1240 lastMousePosition = event->windowPos();
1242 if (!mouseGrabberItem &&
1243 event->type() == QEvent::MouseButtonPress &&
1244 (event->buttons() & event->button()) == event->buttons()) {
1245 if (deliverInitialMousePressEvent(rootItem, event))
1249 return event->isAccepted();
1252 if (mouseGrabberItem) {
1253 QPointF localPos = mouseGrabberItem->mapFromScene(event->windowPos());
1254 QScopedPointer<QMouseEvent> me(cloneMouseEvent(event, &localPos));
1256 q->sendEvent(mouseGrabberItem, me.data());
1257 event->setAccepted(me->isAccepted());
1258 if (me->isAccepted())
1266 void QQuickWindow::mousePressEvent(QMouseEvent *event)
1270 qWarning() << "QQuickWindow::mousePressEvent()" << event->localPos() << event->button() << event->buttons();
1273 d->deliverMouseEvent(event);
1277 void QQuickWindow::mouseReleaseEvent(QMouseEvent *event)
1281 qWarning() << "QQuickWindow::mouseReleaseEvent()" << event->localPos() << event->button() << event->buttons();
1284 if (!d->mouseGrabberItem) {
1285 QWindow::mouseReleaseEvent(event);
1289 d->deliverMouseEvent(event);
1290 if (d->mouseGrabberItem && !event->buttons())
1291 d->mouseGrabberItem->ungrabMouse();
1295 void QQuickWindow::mouseDoubleClickEvent(QMouseEvent *event)
1299 qWarning() << "QQuickWindow::mouseDoubleClickEvent()" << event->localPos() << event->button() << event->buttons();
1302 if (!d->mouseGrabberItem && (event->buttons() & event->button()) == event->buttons()) {
1303 if (d->deliverInitialMousePressEvent(d->rootItem, event))
1310 d->deliverMouseEvent(event);
1313 bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
1314 const QPointF &scenePos, const QPointF &lastScenePos,
1315 Qt::KeyboardModifiers modifiers, bool accepted)
1318 const QTransform transform = QQuickItemPrivate::get(item)->windowToItemTransform();
1320 //create copy of event
1321 QHoverEvent hoverEvent(type, transform.map(scenePos), transform.map(lastScenePos), modifiers);
1322 hoverEvent.setAccepted(accepted);
1324 q->sendEvent(item, &hoverEvent);
1326 return hoverEvent.isAccepted();
1330 void QQuickWindow::mouseMoveEvent(QMouseEvent *event)
1334 qWarning() << "QQuickWindow::mouseMoveEvent()" << event->localPos() << event->button() << event->buttons();
1337 #ifndef QT_NO_CURSOR
1338 d->updateCursor(event->windowPos());
1341 if (!d->mouseGrabberItem) {
1342 if (d->lastMousePosition.isNull())
1343 d->lastMousePosition = event->windowPos();
1344 QPointF last = d->lastMousePosition;
1345 d->lastMousePosition = event->windowPos();
1347 bool accepted = event->isAccepted();
1348 bool delivered = d->deliverHoverEvent(d->rootItem, event->windowPos(), last, event->modifiers(), accepted);
1350 //take care of any exits
1351 accepted = d->clearHover();
1353 event->setAccepted(accepted);
1357 d->deliverMouseEvent(event);
1360 bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
1361 Qt::KeyboardModifiers modifiers, bool &accepted)
1363 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1365 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1366 QPointF p = item->mapFromScene(scenePos);
1367 if (!item->contains(p))
1371 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1372 for (int ii = children.count() - 1; ii >= 0; --ii) {
1373 QQuickItem *child = children.at(ii);
1374 if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled)
1376 if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, accepted))
1380 if (itemPrivate->hoverEnabled) {
1381 QPointF p = item->mapFromScene(scenePos);
1382 if (item->contains(p)) {
1383 if (!hoverItems.isEmpty() && hoverItems[0] == item) {
1385 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1387 QList<QQuickItem *> itemsToHover;
1388 QQuickItem* parent = item;
1389 itemsToHover << item;
1390 while ((parent = parent->parentItem()))
1391 itemsToHover << parent;
1393 // Leaving from previous hovered items until we reach the item or one of its ancestors.
1394 while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems[0])) {
1395 sendHoverEvent(QEvent::HoverLeave, hoverItems[0], scenePos, lastScenePos, modifiers, accepted);
1396 hoverItems.removeFirst();
1399 if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item
1400 // ### Shouldn't we send moves for the parent items as well?
1401 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1403 // Enter items that are not entered yet.
1405 if (!hoverItems.isEmpty())
1406 startIdx = itemsToHover.indexOf(hoverItems[0]) - 1;
1408 startIdx = itemsToHover.count() - 1;
1410 for (int i = startIdx; i >= 0; i--) {
1411 QQuickItem *itemToHover = itemsToHover[i];
1412 if (QQuickItemPrivate::get(itemToHover)->hoverEnabled) {
1413 hoverItems.prepend(itemToHover);
1414 sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, accepted);
1426 bool QQuickWindowPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event)
1429 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1431 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1432 QPointF p = item->mapFromScene(event->posF());
1433 if (!item->contains(p))
1437 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1438 for (int ii = children.count() - 1; ii >= 0; --ii) {
1439 QQuickItem *child = children.at(ii);
1440 if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled)
1442 if (deliverWheelEvent(child, event))
1446 QPointF p = item->mapFromScene(event->posF());
1448 if (item->contains(p)) {
1449 QWheelEvent wheel(p, p, event->pixelDelta(), event->angleDelta(), event->delta(),
1450 event->orientation(), event->buttons(), event->modifiers());
1452 q->sendEvent(item, &wheel);
1453 if (wheel.isAccepted()) {
1462 #ifndef QT_NO_WHEELEVENT
1464 void QQuickWindow::wheelEvent(QWheelEvent *event)
1468 qWarning() << "QQuickWindow::wheelEvent()" << event->pixelDelta() << event->angleDelta();
1471 //if the actual wheel event was accepted, accept the compatability wheel event and return early
1472 if (d->lastWheelEventAccepted && event->angleDelta().isNull())
1476 d->deliverWheelEvent(d->rootItem, event);
1477 d->lastWheelEventAccepted = event->isAccepted();
1479 #endif // QT_NO_WHEELEVENT
1482 bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event)
1485 qWarning("touchCancelEvent");
1488 // A TouchCancel event will typically not contain any points.
1489 // Deliver it to all items that have active touches.
1490 QSet<QQuickItem *> cancelDelivered;
1491 foreach (QQuickItem *item, itemForTouchPointId) {
1492 if (cancelDelivered.contains(item))
1494 cancelDelivered.insert(item);
1495 q->sendEvent(item, event);
1498 if (mouseGrabberItem)
1499 mouseGrabberItem->ungrabMouse();
1500 // The next touch event can only be a TouchBegin so clean up.
1501 itemForTouchPointId.clear();
1505 // check what kind of touch we have (begin/update) and
1506 // call deliverTouchPoints to actually dispatch the points
1507 bool QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event)
1510 if (event->type() == QEvent::TouchBegin)
1511 qWarning() << "touchBeginEvent";
1512 else if (event->type() == QEvent::TouchUpdate)
1513 qWarning() << "touchUpdateEvent points";
1514 else if (event->type() == QEvent::TouchEnd)
1515 qWarning("touchEndEvent");
1518 // List of all items that received an event before
1519 // When we have TouchBegin this is and will stay empty
1520 QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > updatedPoints;
1522 // Figure out who accepted a touch point last and put it in updatedPoints
1523 // Add additional item to newPoints
1524 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
1525 QList<QTouchEvent::TouchPoint> newPoints;
1526 for (int i=0; i<touchPoints.count(); i++) {
1527 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
1528 if (touchPoint.state() == Qt::TouchPointPressed) {
1529 newPoints << touchPoint;
1531 // TouchPointStationary is relevant only to items which
1532 // are also receiving touch points with some other state.
1533 // But we have not yet decided which points go to which item,
1534 // so for now we must include all non-new points in updatedPoints.
1535 if (itemForTouchPointId.contains(touchPoint.id())) {
1536 QQuickItem *item = itemForTouchPointId.value(touchPoint.id());
1538 updatedPoints[item].append(touchPoint);
1543 // Deliver the event, but only if there is at least one new point
1544 // or some item accepted a point and should receive an update
1545 if (newPoints.count() > 0 || updatedPoints.count() > 0) {
1546 QSet<int> acceptedNewPoints;
1547 event->setAccepted(deliverTouchPoints(rootItem, event, newPoints, &acceptedNewPoints, &updatedPoints));
1551 // Remove released points from itemForTouchPointId
1552 if (event->touchPointStates() & Qt::TouchPointReleased) {
1553 for (int i=0; i<touchPoints.count(); i++) {
1554 if (touchPoints[i].state() == Qt::TouchPointReleased) {
1555 itemForTouchPointId.remove(touchPoints[i].id());
1556 if (touchPoints[i].id() == touchMouseId)
1562 if (event->type() == QEvent::TouchEnd) {
1563 Q_ASSERT(itemForTouchPointId.isEmpty());
1566 return event->isAccepted();
1569 // This function recurses and sends the events to the individual items
1570 bool QQuickWindowPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints, QSet<int> *acceptedNewPoints, QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *updatedPoints)
1572 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1574 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1575 for (int i=0; i<newPoints.count(); i++) {
1576 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1577 if (!item->contains(p))
1582 // Check if our children want the event (or parts of it)
1583 // This is the only point where touch event delivery recurses!
1584 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1585 for (int ii = children.count() - 1; ii >= 0; --ii) {
1586 QQuickItem *child = children.at(ii);
1587 if (!child->isEnabled() || !child->isVisible() || QQuickItemPrivate::get(child)->culled)
1589 if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints))
1593 // None of the children accepted the event, so check the given item itself.
1594 // First, construct matchingPoints as a list of TouchPoints which the
1595 // given item might be interested in. Any newly-pressed point which is
1596 // inside the item's bounds will be interesting, and also any updated point
1597 // which was already accepted by that item when it was first pressed.
1598 // (A point which was already accepted is effectively "grabbed" by the item.)
1600 // set of IDs of "interesting" new points
1601 QSet<int> matchingNewPoints;
1602 // set of points which this item has previously accepted, for starters
1603 QList<QTouchEvent::TouchPoint> matchingPoints = (*updatedPoints)[item];
1604 // now add the new points which are inside this item's bounds
1605 if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) {
1606 for (int i = 0; i < newPoints.count(); i++) {
1607 if (acceptedNewPoints->contains(newPoints[i].id()))
1609 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1610 if (item->contains(p)) {
1611 matchingNewPoints.insert(newPoints[i].id());
1612 matchingPoints << newPoints[i];
1616 // If there are no matching new points, and the existing points are all stationary,
1617 // there's no need to send an event to this item. This is required by a test in
1618 // tst_qquickwindow::touchEvent_basic:
1619 // a single stationary press on an item shouldn't cause an event
1620 if (matchingNewPoints.isEmpty()) {
1621 bool stationaryOnly = true;
1622 Q_FOREACH (QTouchEvent::TouchPoint tp, matchingPoints)
1623 if (tp.state() != Qt::TouchPointStationary)
1624 stationaryOnly = false;
1626 matchingPoints.clear();
1629 if (!matchingPoints.isEmpty()) {
1630 // Now we know this item might be interested in the event. Copy and send it, but
1631 // with only the subset of TouchPoints which are relevant to that item: that's matchingPoints.
1632 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1633 transformTouchPoints(matchingPoints, itemPrivate->windowToItemTransform());
1634 deliverMatchingPointsToItem(item, event, acceptedNewPoints, matchingNewPoints, matchingPoints);
1637 // record the fact that this item has been visited already
1638 updatedPoints->remove(item);
1640 // recursion is done only if ALL touch points have been delivered
1641 return (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty());
1644 // touchEventForItemBounds has no means to generate a touch event that contains
1645 // only the points that are relevant for this item. Thus the need for
1646 // matchingPoints to already be that set of interesting points.
1647 // They are all pre-transformed, too.
1648 bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints)
1650 QScopedPointer<QTouchEvent> touchEvent(touchEventWithPoints(*event, matchingPoints));
1651 touchEvent.data()->setTarget(item);
1652 bool touchEventAccepted = false;
1654 // First check whether the parent wants to be a filter,
1655 // and if the parent accepts the event we are done.
1656 if (sendFilteredTouchEvent(item->parentItem(), item, event)) {
1661 // Since it can change in sendEvent, update itemForTouchPointId now
1662 foreach (int id, matchingNewPoints)
1663 itemForTouchPointId[id] = item;
1665 // Deliver the touch event to the given item
1666 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1667 itemPrivate->deliverTouchEvent(touchEvent.data());
1668 touchEventAccepted = touchEvent->isAccepted();
1670 // If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it.
1671 if (!touchEventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) {
1673 event->setAccepted(translateTouchToMouse(item, event));
1674 if (event->isAccepted()) {
1675 touchEventAccepted = true;
1679 if (touchEventAccepted) {
1680 // If the touch was accepted (regardless by whom or in what form),
1681 // update acceptedNewPoints.
1682 foreach (int id, matchingNewPoints)
1683 acceptedNewPoints->insert(id);
1685 // But if the event was not accepted then we know this item
1686 // will not be interested in further updates for those touchpoint IDs either.
1687 foreach (int id, matchingNewPoints)
1688 if (itemForTouchPointId[id] == item)
1689 itemForTouchPointId.remove(id);
1692 return touchEventAccepted;
1695 QTouchEvent *QQuickWindowPrivate::touchEventForItemBounds(QQuickItem *target, const QTouchEvent &originalEvent)
1697 const QList<QTouchEvent::TouchPoint> &touchPoints = originalEvent.touchPoints();
1698 QList<QTouchEvent::TouchPoint> pointsInBounds;
1699 // if all points are stationary, the list of points should be empty to signal a no-op
1700 if (originalEvent.touchPointStates() != Qt::TouchPointStationary) {
1701 for (int i = 0; i < touchPoints.count(); ++i) {
1702 const QTouchEvent::TouchPoint &tp = touchPoints.at(i);
1703 if (tp.state() == Qt::TouchPointPressed) {
1704 QPointF p = target->mapFromScene(tp.scenePos());
1705 if (target->contains(p))
1706 pointsInBounds.append(tp);
1708 pointsInBounds.append(tp);
1711 transformTouchPoints(pointsInBounds, QQuickItemPrivate::get(target)->windowToItemTransform());
1714 QTouchEvent* touchEvent = touchEventWithPoints(originalEvent, pointsInBounds);
1715 touchEvent->setTarget(target);
1719 QTouchEvent *QQuickWindowPrivate::touchEventWithPoints(const QTouchEvent &event, const QList<QTouchEvent::TouchPoint> &newPoints)
1721 Qt::TouchPointStates eventStates;
1722 for (int i=0; i<newPoints.count(); i++)
1723 eventStates |= newPoints[i].state();
1724 // if all points have the same state, set the event type accordingly
1725 QEvent::Type eventType = event.type();
1726 switch (eventStates) {
1727 case Qt::TouchPointPressed:
1728 eventType = QEvent::TouchBegin;
1730 case Qt::TouchPointReleased:
1731 eventType = QEvent::TouchEnd;
1734 eventType = QEvent::TouchUpdate;
1738 QTouchEvent *touchEvent = new QTouchEvent(eventType);
1739 touchEvent->setWindow(event.window());
1740 touchEvent->setTarget(event.target());
1741 touchEvent->setDevice(event.device());
1742 touchEvent->setModifiers(event.modifiers());
1743 touchEvent->setTouchPoints(newPoints);
1744 touchEvent->setTouchPointStates(eventStates);
1745 touchEvent->setTimestamp(event.timestamp());
1746 touchEvent->accept();
1750 #ifndef QT_NO_DRAGANDDROP
1751 void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event)
1754 grabber->resetTarget();
1755 QQuickDragGrabber::iterator grabItem = grabber->begin();
1756 if (grabItem != grabber->end()) {
1757 Q_ASSERT(event->type() != QEvent::DragEnter);
1758 if (event->type() == QEvent::Drop) {
1759 QDropEvent *e = static_cast<QDropEvent *>(event);
1760 for (e->setAccepted(false); !e->isAccepted() && grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1761 QPointF p = (**grabItem)->mapFromScene(e->pos());
1762 QDropEvent translatedEvent(
1764 e->possibleActions(),
1767 e->keyboardModifiers());
1768 QQuickDropEventEx::copyActions(&translatedEvent, *e);
1769 q->sendEvent(**grabItem, &translatedEvent);
1770 e->setAccepted(translatedEvent.isAccepted());
1771 e->setDropAction(translatedEvent.dropAction());
1772 grabber->setTarget(**grabItem);
1775 if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave.
1776 QDragLeaveEvent leaveEvent;
1777 for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
1778 q->sendEvent(**grabItem, &leaveEvent);
1780 } else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1781 QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event);
1782 if (deliverDragEvent(grabber, **grabItem, moveEvent)) {
1783 moveEvent->setAccepted(true);
1784 for (++grabItem; grabItem != grabber->end();) {
1785 QPointF p = (**grabItem)->mapFromScene(moveEvent->pos());
1786 if ((**grabItem)->contains(p)) {
1787 QDragMoveEvent translatedEvent(
1789 moveEvent->possibleActions(),
1790 moveEvent->mimeData(),
1791 moveEvent->mouseButtons(),
1792 moveEvent->keyboardModifiers());
1793 QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent);
1794 q->sendEvent(**grabItem, &translatedEvent);
1797 QDragLeaveEvent leaveEvent;
1798 q->sendEvent(**grabItem, &leaveEvent);
1799 grabItem = grabber->release(grabItem);
1804 QDragLeaveEvent leaveEvent;
1805 q->sendEvent(**grabItem, &leaveEvent);
1809 if (event->type() == QEvent::DragEnter || event->type() == QEvent::DragMove) {
1810 QDragMoveEvent *e = static_cast<QDragMoveEvent *>(event);
1811 QDragEnterEvent enterEvent(
1813 e->possibleActions(),
1816 e->keyboardModifiers());
1817 QQuickDropEventEx::copyActions(&enterEvent, *e);
1818 event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent));
1822 bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event)
1825 bool accepted = false;
1826 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1827 if (!item->isVisible() || !item->isEnabled() || QQuickItemPrivate::get(item)->culled)
1830 QPointF p = item->mapFromScene(event->pos());
1831 if (item->contains(p)) {
1832 if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) {
1833 QDragMoveEvent translatedEvent(
1835 event->possibleActions(),
1837 event->mouseButtons(),
1838 event->keyboardModifiers(),
1840 QQuickDropEventEx::copyActions(&translatedEvent, *event);
1841 q->sendEvent(item, &translatedEvent);
1842 if (event->type() == QEvent::DragEnter) {
1843 if (translatedEvent.isAccepted()) {
1844 grabber->grab(item);
1851 } else if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1855 QDragEnterEvent enterEvent(
1857 event->possibleActions(),
1859 event->mouseButtons(),
1860 event->keyboardModifiers());
1861 QQuickDropEventEx::copyActions(&enterEvent, *event);
1862 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1863 for (int ii = children.count() - 1; ii >= 0; --ii) {
1864 if (deliverDragEvent(grabber, children.at(ii), &enterEvent))
1870 #endif // QT_NO_DRAGANDDROP
1872 #ifndef QT_NO_CURSOR
1873 void QQuickWindowPrivate::updateCursor(const QPointF &scenePos)
1877 QQuickItem *oldCursorItem = cursorItem;
1878 cursorItem = findCursorItem(rootItem, scenePos);
1880 if (cursorItem != oldCursorItem) {
1882 q->setCursor(cursorItem->cursor());
1888 QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF &scenePos)
1890 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1891 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1892 QPointF p = item->mapFromScene(scenePos);
1893 if (!item->contains(p))
1897 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1898 for (int ii = children.count() - 1; ii >= 0; --ii) {
1899 QQuickItem *child = children.at(ii);
1900 if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled)
1902 if (QQuickItem *cursorItem = findCursorItem(child, scenePos))
1906 if (itemPrivate->hasCursor) {
1907 QPointF p = item->mapFromScene(scenePos);
1908 if (item->contains(p))
1915 bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event)
1920 QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target);
1921 if (targetPrivate->filtersChildMouseEvents) {
1922 QScopedPointer<QTouchEvent> targetEvent(touchEventForItemBounds(target, *event));
1923 if (!targetEvent->touchPoints().isEmpty()) {
1924 QVector<int> touchIds;
1925 for (int i = 0; i < event->touchPoints().size(); ++i)
1926 touchIds.append(event->touchPoints().at(i).id());
1927 if (target->childMouseEventFilter(item, targetEvent.data())) {
1928 target->grabTouchPoints(touchIds);
1929 if (mouseGrabberItem) {
1930 mouseGrabberItem->ungrabMouse();
1936 // Only offer a mouse event to the filter if we have one point
1937 if (targetEvent->touchPoints().count() == 1) {
1939 const QTouchEvent::TouchPoint &tp = targetEvent->touchPoints().first();
1940 switch (tp.state()) {
1941 case Qt::TouchPointPressed:
1942 t = QEvent::MouseButtonPress;
1944 case Qt::TouchPointReleased:
1945 t = QEvent::MouseButtonRelease;
1948 // move or stationary
1949 t = QEvent::MouseMove;
1953 // targetEvent is already transformed wrt local position, velocity, etc.
1954 QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, targetEvent->touchPoints().first(), event, item, false));
1955 if (target->childMouseEventFilter(item, mouseEvent.data())) {
1956 itemForTouchPointId[tp.id()] = target;
1957 touchMouseId = tp.id();
1958 target->grabMouse();
1965 return sendFilteredTouchEvent(target->parentItem(), item, event);
1968 bool QQuickWindowPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QEvent *event)
1973 QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target);
1974 if (targetPrivate->filtersChildMouseEvents)
1975 if (target->childMouseEventFilter(item, event))
1978 if (sendFilteredMouseEvent(target->parentItem(), item, event))
1984 bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event)
1986 QStyleHints *styleHints = qApp->styleHints();
1987 int caps = QGuiApplicationPrivate::mouseEventCaps(event);
1988 bool dragVelocityLimitAvailable = (caps & QTouchDevice::Velocity)
1989 && styleHints->startDragVelocity();
1990 bool overThreshold = qAbs(d) > styleHints->startDragDistance();
1991 if (dragVelocityLimitAvailable) {
1992 QVector2D velocityVec = QGuiApplicationPrivate::mouseEventVelocity(event);
1993 qreal velocity = axis == Qt::XAxis ? velocityVec.x() : velocityVec.y();
1994 overThreshold |= qAbs(velocity) > styleHints->startDragVelocity();
1996 return overThreshold;
2000 Propagates an event \a e to a QQuickItem \a item on the window.
2002 The return value is currently not used.
2004 bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e)
2009 qWarning("QQuickWindow::sendEvent: Cannot send event to a null item");
2015 switch (e->type()) {
2016 case QEvent::KeyPress:
2017 case QEvent::KeyRelease:
2019 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
2020 while (!e->isAccepted() && (item = item->parentItem())) {
2022 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
2025 case QEvent::FocusIn:
2026 case QEvent::FocusOut:
2027 QQuickItemPrivate::get(item)->deliverFocusEvent(static_cast<QFocusEvent *>(e));
2029 case QEvent::MouseButtonPress:
2030 case QEvent::MouseButtonRelease:
2031 case QEvent::MouseButtonDblClick:
2032 case QEvent::MouseMove:
2033 // XXX todo - should sendEvent be doing this? how does it relate to forwarded events?
2034 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
2035 // accept because qml items by default accept and have to explicitly opt out of accepting
2037 QQuickItemPrivate::get(item)->deliverMouseEvent(static_cast<QMouseEvent *>(e));
2040 case QEvent::UngrabMouse:
2041 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
2043 item->mouseUngrabEvent();
2047 QQuickItemPrivate::get(item)->deliverWheelEvent(static_cast<QWheelEvent *>(e));
2049 case QEvent::HoverEnter:
2050 case QEvent::HoverLeave:
2051 case QEvent::HoverMove:
2052 QQuickItemPrivate::get(item)->deliverHoverEvent(static_cast<QHoverEvent *>(e));
2054 case QEvent::TouchBegin:
2055 case QEvent::TouchUpdate:
2056 case QEvent::TouchEnd:
2057 d->sendFilteredTouchEvent(item->parentItem(), item, static_cast<QTouchEvent *>(e));
2059 case QEvent::TouchCancel:
2060 QQuickItemPrivate::get(item)->deliverTouchEvent(static_cast<QTouchEvent *>(e));
2062 #ifndef QT_NO_DRAGANDDROP
2063 case QEvent::DragEnter:
2064 case QEvent::DragMove:
2065 case QEvent::DragLeave:
2067 QQuickItemPrivate::get(item)->deliverDragEvent(e);
2077 void QQuickWindowPrivate::cleanupNodes()
2079 for (int ii = 0; ii < cleanupNodeList.count(); ++ii)
2080 delete cleanupNodeList.at(ii);
2081 cleanupNodeList.clear();
2084 void QQuickWindowPrivate::cleanupNodesOnShutdown(QQuickItem *item)
2086 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
2087 if (p->itemNodeInstance) {
2088 delete p->itemNodeInstance;
2089 p->itemNodeInstance = 0;
2091 if (p->extra.isAllocated()) {
2092 p->extra->opacityNode = 0;
2093 p->extra->clipNode = 0;
2094 p->extra->rootNode = 0;
2101 for (int ii = 0; ii < p->childItems.count(); ++ii)
2102 cleanupNodesOnShutdown(p->childItems.at(ii));
2105 // This must be called from the render thread, with the main thread frozen
2106 void QQuickWindowPrivate::cleanupNodesOnShutdown()
2110 cleanupNodesOnShutdown(rootItem);
2111 QSet<QQuickItem *>::const_iterator it = parentlessItems.begin();
2112 for (; it != parentlessItems.end(); ++it)
2113 cleanupNodesOnShutdown(*it);
2114 q->cleanupSceneGraph();
2117 void QQuickWindowPrivate::updateDirtyNodes()
2120 qWarning() << "QQuickWindowPrivate::updateDirtyNodes():";
2125 QQuickItem *updateList = dirtyItemList;
2127 if (updateList) QQuickItemPrivate::get(updateList)->prevDirtyItem = &updateList;
2129 while (updateList) {
2130 QQuickItem *item = updateList;
2131 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
2132 itemPriv->removeFromDirtyList();
2135 qWarning() << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString());
2137 updateDirtyNode(item);
2141 void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item)
2143 #ifdef QML_RUNTIME_TESTING
2144 bool didFlash = false;
2147 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
2148 quint32 dirty = itemPriv->dirtyAttributes;
2149 itemPriv->dirtyAttributes = 0;
2151 if ((dirty & QQuickItemPrivate::TransformUpdateMask) ||
2152 (dirty & QQuickItemPrivate::Size && itemPriv->origin() != QQuickItem::TopLeft &&
2153 (itemPriv->scale() != 1. || itemPriv->rotation() != 0.))) {
2157 if (itemPriv->x != 0. || itemPriv->y != 0.)
2158 matrix.translate(itemPriv->x, itemPriv->y);
2160 for (int ii = itemPriv->transforms.count() - 1; ii >= 0; --ii)
2161 itemPriv->transforms.at(ii)->applyTo(&matrix);
2163 if (itemPriv->scale() != 1. || itemPriv->rotation() != 0.) {
2164 QPointF origin = item->transformOriginPoint();
2165 matrix.translate(origin.x(), origin.y());
2166 if (itemPriv->scale() != 1.)
2167 matrix.scale(itemPriv->scale(), itemPriv->scale());
2168 if (itemPriv->rotation() != 0.)
2169 matrix.rotate(itemPriv->rotation(), 0, 0, 1);
2170 matrix.translate(-origin.x(), -origin.y());
2173 itemPriv->itemNode()->setMatrix(matrix);
2176 bool clipEffectivelyChanged = (dirty & (QQuickItemPrivate::Clip | QQuickItemPrivate::Window)) &&
2177 ((item->clip() == false) != (itemPriv->clipNode() == 0));
2178 int effectRefCount = itemPriv->extra.isAllocated()?itemPriv->extra->effectRefCount:0;
2179 bool effectRefEffectivelyChanged = (dirty & (QQuickItemPrivate::EffectReference | QQuickItemPrivate::Window)) &&
2180 ((effectRefCount == 0) != (itemPriv->rootNode() == 0));
2182 if (clipEffectivelyChanged) {
2183 QSGNode *parent = itemPriv->opacityNode() ? (QSGNode *) itemPriv->opacityNode() :
2184 (QSGNode *)itemPriv->itemNode();
2185 QSGNode *child = itemPriv->rootNode() ? (QSGNode *)itemPriv->rootNode() :
2186 (QSGNode *)itemPriv->groupNode;
2189 Q_ASSERT(itemPriv->clipNode() == 0);
2190 itemPriv->extra.value().clipNode = new QQuickDefaultClipNode(item->clipRect());
2191 itemPriv->clipNode()->update();
2194 parent->removeChildNode(child);
2195 parent->appendChildNode(itemPriv->clipNode());
2197 itemPriv->clipNode()->appendChildNode(child);
2200 Q_ASSERT(itemPriv->clipNode() != 0);
2201 parent->removeChildNode(itemPriv->clipNode());
2203 itemPriv->clipNode()->removeChildNode(child);
2204 delete itemPriv->clipNode();
2205 itemPriv->extra->clipNode = 0;
2207 parent->appendChildNode(child);
2211 if (dirty & QQuickItemPrivate::ChildrenUpdateMask)
2212 itemPriv->childContainerNode()->removeAllChildNodes();
2214 if (effectRefEffectivelyChanged) {
2215 QSGNode *parent = itemPriv->clipNode();
2217 parent = itemPriv->opacityNode();
2219 parent = itemPriv->itemNode();
2220 QSGNode *child = itemPriv->groupNode;
2222 if (itemPriv->extra.isAllocated() && itemPriv->extra->effectRefCount) {
2223 Q_ASSERT(itemPriv->rootNode() == 0);
2224 itemPriv->extra->rootNode = new QSGRootNode;
2227 parent->removeChildNode(child);
2228 parent->appendChildNode(itemPriv->rootNode());
2230 itemPriv->rootNode()->appendChildNode(child);
2232 Q_ASSERT(itemPriv->rootNode() != 0);
2233 parent->removeChildNode(itemPriv->rootNode());
2235 itemPriv->rootNode()->removeChildNode(child);
2236 delete itemPriv->rootNode();
2237 itemPriv->extra->rootNode = 0;
2239 parent->appendChildNode(child);
2243 if (dirty & QQuickItemPrivate::ChildrenUpdateMask) {
2244 QSGNode *groupNode = itemPriv->groupNode;
2246 groupNode->removeAllChildNodes();
2248 QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems();
2251 for (; ii < orderedChildren.count() && orderedChildren.at(ii)->z() < 0; ++ii) {
2252 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
2253 if (!childPrivate->explicitVisible &&
2254 (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount))
2256 if (childPrivate->itemNode()->parent())
2257 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
2259 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
2262 QSGNode *beforePaintNode = itemPriv->groupNode ? itemPriv->groupNode->lastChild() : 0;
2263 if (beforePaintNode || itemPriv->extra.isAllocated())
2264 itemPriv->extra.value().beforePaintNode = beforePaintNode;
2266 if (itemPriv->paintNode)
2267 itemPriv->childContainerNode()->appendChildNode(itemPriv->paintNode);
2269 for (; ii < orderedChildren.count(); ++ii) {
2270 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
2271 if (!childPrivate->explicitVisible &&
2272 (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount))
2274 if (childPrivate->itemNode()->parent())
2275 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
2277 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
2281 if ((dirty & QQuickItemPrivate::Size) && itemPriv->clipNode()) {
2282 itemPriv->clipNode()->setRect(item->clipRect());
2283 itemPriv->clipNode()->update();
2286 if (dirty & (QQuickItemPrivate::OpacityValue | QQuickItemPrivate::Visible
2287 | QQuickItemPrivate::HideReference | QQuickItemPrivate::Window))
2289 qreal opacity = itemPriv->explicitVisible && (!itemPriv->extra.isAllocated() || itemPriv->extra->hideRefCount == 0)
2290 ? itemPriv->opacity() : qreal(0);
2292 if (opacity != 1 && !itemPriv->opacityNode()) {
2293 itemPriv->extra.value().opacityNode = new QSGOpacityNode;
2295 QSGNode *parent = itemPriv->itemNode();
2296 QSGNode *child = itemPriv->clipNode();
2298 child = itemPriv->rootNode();
2300 child = itemPriv->groupNode;
2303 parent->removeChildNode(child);
2304 parent->appendChildNode(itemPriv->opacityNode());
2306 itemPriv->opacityNode()->appendChildNode(child);
2308 if (itemPriv->opacityNode())
2309 itemPriv->opacityNode()->setOpacity(opacity);
2312 if (dirty & QQuickItemPrivate::ContentUpdateMask) {
2314 if (itemPriv->flags & QQuickItem::ItemHasContents) {
2315 updatePaintNodeData.transformNode = itemPriv->itemNode();
2316 itemPriv->paintNode = item->updatePaintNode(itemPriv->paintNode, &updatePaintNodeData);
2318 Q_ASSERT(itemPriv->paintNode == 0 ||
2319 itemPriv->paintNode->parent() == 0 ||
2320 itemPriv->paintNode->parent() == itemPriv->childContainerNode());
2322 if (itemPriv->paintNode && itemPriv->paintNode->parent() == 0) {
2323 if (itemPriv->extra.isAllocated() && itemPriv->extra->beforePaintNode)
2324 itemPriv->childContainerNode()->insertChildNodeAfter(itemPriv->paintNode, itemPriv->extra->beforePaintNode);
2326 itemPriv->childContainerNode()->prependChildNode(itemPriv->paintNode);
2328 } else if (itemPriv->paintNode) {
2329 delete itemPriv->paintNode;
2330 itemPriv->paintNode = 0;
2335 // Check consistency.
2336 const QSGNode *nodeChain[] = {
2337 itemPriv->itemNodeInstance,
2338 itemPriv->opacityNode(),
2339 itemPriv->clipNode(),
2340 itemPriv->rootNode(),
2341 itemPriv->groupNode,
2342 itemPriv->paintNode,
2347 while (ip < 5 && nodeChain[ip] == 0)
2352 while (ic < 5 && nodeChain[ic] == 0)
2354 const QSGNode *parent = nodeChain[ip];
2355 const QSGNode *child = nodeChain[ic];
2357 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 0);
2359 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 1);
2360 Q_ASSERT(child->parent() == parent);
2361 bool containsChild = false;
2362 for (QSGNode *n = parent->firstChild(); n; n = n->nextSibling())
2363 containsChild |= (n == child);
2364 Q_ASSERT(containsChild);
2370 #ifdef QML_RUNTIME_TESTING
2371 if (itemPriv->sceneGraphContext()->isFlashModeEnabled()) {
2372 QSGFlashNode *flash = new QSGFlashNode();
2373 flash->setRect(item->boundingRect());
2374 itemPriv->childContainerNode()->appendChildNode(flash);
2385 void QQuickWindow::maybeUpdate()
2388 d->windowManager->maybeUpdate(this);
2391 void QQuickWindow::cleanupSceneGraph()
2398 delete d->renderer->rootNode();
2405 Returns the opengl context used for rendering.
2407 If the scene graph is not ready, this function will return 0.
2409 \sa sceneGraphInitialized(), sceneGraphInvalidated()
2412 QOpenGLContext *QQuickWindow::openglContext() const
2414 Q_D(const QQuickWindow);
2415 if (d->context->isReady())
2416 return d->context->glContext();
2421 \fn void QQuickWindow::frameSwapped()
2423 This signal is emitted when the frame buffers have been swapped.
2425 This signal will be emitted from the scene graph rendering thread.
2430 \fn void QQuickWindow::sceneGraphInitialized()
2432 This signal is emitted when the scene graph has been initialized.
2434 This signal will be emitted from the scene graph rendering thread.
2440 \fn void QQuickWindow::sceneGraphInvalidated()
2442 This signal is emitted when the scene graph has been invalidated.
2444 This signal implies that the opengl rendering context used
2445 has been invalidated and all user resources tied to that context
2448 This signal will be emitted from the scene graph rendering thread.
2453 Sets the render target for this window to be \a fbo.
2455 The specified fbo must be created in the context of the window
2456 or one that shares with it.
2459 This function can only be called from the thread doing
2463 void QQuickWindow::setRenderTarget(QOpenGLFramebufferObject *fbo)
2466 if (d->context && d->context && QThread::currentThread() != d->context->thread()) {
2467 qWarning("QQuickWindow::setRenderThread: Cannot set render target from outside the rendering thread");
2471 d->renderTarget = fbo;
2473 d->renderTargetId = fbo->handle();
2474 d->renderTargetSize = fbo->size();
2476 d->renderTargetId = 0;
2477 d->renderTargetSize = QSize();
2484 Sets the render target for this window to be an FBO with
2485 \a fboId and \a size.
2487 The specified FBO must be created in the context of the window
2488 or one that shares with it.
2491 This function can only be called from the thread doing
2495 void QQuickWindow::setRenderTarget(uint fboId, const QSize &size)
2498 if (d->context && d->context && QThread::currentThread() != d->context->thread()) {
2499 qWarning("QQuickWindow::setRenderThread: Cannot set render target from outside the rendering thread");
2503 d->renderTargetId = fboId;
2504 d->renderTargetSize = size;
2506 // Unset any previously set instance...
2507 d->renderTarget = 0;
2512 Returns the FBO id of the render target when set; otherwise returns 0.
2514 uint QQuickWindow::renderTargetId() const
2516 Q_D(const QQuickWindow);
2517 return d->renderTargetId;
2521 Returns the size of the currently set render target; otherwise returns an enpty size.
2523 QSize QQuickWindow::renderTargetSize() const
2525 Q_D(const QQuickWindow);
2526 return d->renderTargetSize;
2533 Returns the render target for this window.
2535 The default is to render to the surface of the window, in which
2536 case the render target is 0.
2538 QOpenGLFramebufferObject *QQuickWindow::renderTarget() const
2540 Q_D(const QQuickWindow);
2541 return d->renderTarget;
2546 Grabs the contents of the window and returns it as an image.
2548 This function might not work if the window is not visible.
2550 \warning Calling this function will cause performance problems.
2552 \warning This function can only be called from the GUI thread.
2554 QImage QQuickWindow::grabWindow()
2557 return d->windowManager->grab(this);
2561 Returns an incubation controller that splices incubation between frames
2562 for this window. QQuickView automatically installs this controller for you,
2563 otherwise you will need to install it yourself using \l{QQmlEngine::setIncubationController()}.
2565 The controller is owned by the window and will be destroyed when the window
2568 QQmlIncubationController *QQuickWindow::incubationController() const
2570 Q_D(const QQuickWindow);
2572 if (!d->incubationController)
2573 d->incubationController = new QQuickWindowIncubationController(this);
2574 return d->incubationController;
2580 \enum QQuickWindow::CreateTextureOption
2582 The CreateTextureOption enums are used to customize a texture is wrapped.
2584 \value TextureHasAlphaChannel The texture has an alpha channel and should
2585 be drawn using blending.
2587 \value TextureHasMipmaps The texture has mipmaps and can be drawn with
2590 \value TextureOwnsGLTexture The texture object owns the texture id and
2591 will delete the GL texture when the texture object is deleted.
2595 \fn void QQuickWindow::beforeSynchronizing()
2597 This signal is emitted before the scene graph is synchronized with the QML state.
2599 This signal can be used to do any preparation required before calls to
2600 QQuickItem::updatePaintNode().
2602 The GL context used for rendering the scene graph will be bound at this point.
2604 \warning This signal is emitted from the scene graph rendering thread. If your
2605 slot function needs to finish before execution continues, you must make sure that
2606 the connection is direct (see Qt::ConnectionType).
2608 \warning Make very sure that a signal handler for beforeSynchronizing leaves the GL
2609 context in the same state as it was when the signal handler was entered. Failing to
2610 do so can result in the scene not rendering properly.
2614 \fn void QQuickWindow::beforeRendering()
2616 This signal is emitted before the scene starts rendering.
2618 Combined with the modes for clearing the background, this option
2619 can be used to paint using raw GL under QML content.
2621 The GL context used for rendering the scene graph will be bound
2624 \warning This signal is emitted from the scene graph rendering thread. If your
2625 slot function needs to finish before execution continues, you must make sure that
2626 the connection is direct (see Qt::ConnectionType).
2628 \warning Make very sure that a signal handler for beforeRendering leaves the GL
2629 context in the same state as it was when the signal handler was entered. Failing to
2630 do so can result in the scene not rendering properly.
2634 \fn void QQuickWindow::afterRendering()
2636 This signal is emitted after the scene has completed rendering, before swapbuffers is called.
2638 This signal can be used to paint using raw GL on top of QML content,
2639 or to do screen scraping of the current frame buffer.
2641 The GL context used for rendering the scene graph will be bound at this point.
2643 \warning This signal is emitted from the scene graph rendering thread. If your
2644 slot function needs to finish before execution continues, you must make sure that
2645 the connection is direct (see Qt::ConnectionType).
2647 \warning Make very sure that a signal handler for afterRendering() leaves the GL
2648 context in the same state as it was when the signal handler was entered. Failing to
2649 do so can result in the scene not rendering properly.
2655 Sets weither the scene graph rendering of QML should clear the color buffer
2656 before it starts rendering to \a enabled.
2658 By disabling clearing of the color buffer, it is possible to do GL painting
2659 under the scene graph.
2661 The color buffer is cleared by default.
2663 \sa beforeRendering()
2666 void QQuickWindow::setClearBeforeRendering(bool enabled)
2669 d->clearBeforeRendering = enabled;
2675 Returns weither clearing of the color buffer is done before rendering or not.
2678 bool QQuickWindow::clearBeforeRendering() const
2680 Q_D(const QQuickWindow);
2681 return d->clearBeforeRendering;
2687 Creates a new QSGTexture from the supplied \a image. If the image has an
2688 alpha channel, the corresponding texture will have an alpha channel.
2690 The caller of the function is responsible for deleting the returned texture.
2691 The actual GL texture will be deleted when the texture object is deleted.
2693 Depending on the underlying implementation of the scene graph, the returned
2694 texture may be part of an atlas. For code to be portable across implementations
2695 one should always use the texture coordinates returned from
2696 QSGTexture::normalizedTextureSubRect() when building geometry.
2698 \warning This function will return 0 if the scene graph has not yet been
2701 \warning The returned texture is not memory managed by the scene graph and
2702 must be explicitly deleted by the caller on the rendering thread.
2703 This is acheived by deleting the texture from a QSGNode destructor
2704 or by using deleteLater() in the case where the texture already has affinity
2705 to the rendering thread.
2707 This function can be called from any thread.
2709 \sa sceneGraphInitialized()
2712 QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const
2714 Q_D(const QQuickWindow);
2715 if (d->context && d->context->isReady())
2716 return d->context->createTexture(image);
2724 Creates a new QSGTexture object from an existing GL texture \a id and \a size.
2726 The caller of the function is responsible for deleting the returned texture.
2728 Use \a options to customize the texture attributes.
2730 \warning This function will return 0 if the scenegraph has not yet been
2733 \sa sceneGraphInitialized()
2735 QSGTexture *QQuickWindow::createTextureFromId(uint id, const QSize &size, CreateTextureOptions options) const
2737 Q_D(const QQuickWindow);
2738 if (d->context && d->context->isReady()) {
2739 QSGPlainTexture *texture = new QSGPlainTexture();
2740 texture->setTextureId(id);
2741 texture->setHasAlphaChannel(options & TextureHasAlphaChannel);
2742 texture->setHasMipmaps(options & TextureHasMipmaps);
2743 texture->setOwnsTexture(options & TextureOwnsGLTexture);
2744 texture->setTextureSize(size);
2751 \property QQuickWindow::color
2752 \brief The color used to clear the OpenGL context.
2754 Setting the clear color has no effect when clearing is disabled.
2755 By default, the clear color is white.
2757 \sa setClearBeforeRendering()
2760 void QQuickWindow::setColor(const QColor &color)
2763 if (color == d->clearColor)
2766 d->clearColor = color;
2767 emit colorChanged(color);
2770 QColor QQuickWindow::color() const
2772 return d_func()->clearColor;
2777 #include "moc_qquickwindow.cpp"