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)
330 , touchMousePressTimestamp(0)
331 , renderWithoutShowing(false)
336 , clearColor(Qt::white)
337 , clearBeforeRendering(true)
338 , persistentGLContext(false)
339 , persistentSceneGraph(false)
340 , lastWheelEventAccepted(false)
343 , incubationController(0)
347 QQuickWindowPrivate::~QQuickWindowPrivate()
351 void QQuickWindowPrivate::init(QQuickWindow *c)
357 rootItem = new QQuickRootItem;
358 QQmlEngine::setObjectOwnership(rootItem, QQmlEngine::CppOwnership);
359 QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(rootItem);
360 rootItemPrivate->window = q;
361 rootItemPrivate->windowRefCount = 1;
362 rootItemPrivate->flags |= QQuickItem::ItemIsFocusScope;
364 // In the absence of a focus in event on some platforms assume the window will
365 // be activated immediately and set focus on the rootItem
366 // ### Remove when QTBUG-22415 is resolved.
367 //It is important that this call happens after the rootItem has a window..
368 rootItem->setFocus(true);
370 windowManager = QQuickWindowManager::instance();
371 context = windowManager->sceneGraphContext();
372 q->setSurfaceType(QWindow::OpenGLSurface);
373 q->setFormat(context->defaultSurfaceFormat());
375 QObject::connect(context, SIGNAL(initialized()), q, SIGNAL(sceneGraphInitialized()), Qt::DirectConnection);
376 QObject::connect(context, SIGNAL(invalidated()), q, SIGNAL(sceneGraphInvalidated()), Qt::DirectConnection);
377 QObject::connect(context, SIGNAL(invalidated()), q, SLOT(cleanupSceneGraph()), Qt::DirectConnection);
380 QQmlListProperty<QObject> QQuickWindowPrivate::data()
383 return QQuickItemPrivate::get(rootItem)->data();
386 void QQuickWindowPrivate::initRootItem()
389 q->connect(q, SIGNAL(widthChanged(int)),
390 rootItem, SLOT(setWidth(int)));
391 q->connect(q, SIGNAL(heightChanged(int)),
392 rootItem, SLOT(setHeight(int)));
393 rootItem->setWidth(q->width());
394 rootItem->setHeight(q->height());
397 static QMouseEvent *touchToMouseEvent(QEvent::Type type, const QTouchEvent::TouchPoint &p, QTouchEvent *event, QQuickItem *item, bool transformNeeded = true)
399 // The touch point local position and velocity are not yet transformed.
400 QMouseEvent *me = new QMouseEvent(type, transformNeeded ? item->mapFromScene(p.scenePos()) : p.pos(), p.scenePos(), p.screenPos(),
401 Qt::LeftButton, Qt::LeftButton, event->modifiers());
402 me->setAccepted(true);
403 me->setTimestamp(event->timestamp());
404 QVector2D transformedVelocity = p.velocity();
405 if (transformNeeded) {
406 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
407 QMatrix4x4 transformMatrix(itemPrivate->windowToItemTransform());
408 transformedVelocity = transformMatrix.mapVector(p.velocity()).toVector2D();
410 QGuiApplicationPrivate::setMouseEventCapsAndVelocity(me, event->device()->capabilities(), transformedVelocity);
414 bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *event)
417 // For each point, check if it is accepted, if not, try the next point.
418 // Any of the fingers can become the mouse one.
419 // This can happen because a mouse area might not accept an event at some point but another.
420 for (int i = 0; i < event->touchPoints().count(); ++i) {
421 const QTouchEvent::TouchPoint &p = event->touchPoints().at(i);
423 if (touchMouseId == -1 && p.state() & Qt::TouchPointPressed) {
424 QPointF pos = item->mapFromScene(p.scenePos());
426 // probably redundant, we check bounds in the calling function (matchingNewPoints)
427 if (!item->contains(pos))
430 bool doubleClick = event->timestamp() - touchMousePressTimestamp
431 < static_cast<ulong>(qApp->styleHints()->mouseDoubleClickInterval());
432 touchMousePressTimestamp = event->timestamp();
433 // Store the id already here and restore it to -1 if the event does not get
434 // accepted. Cannot defer setting the new value because otherwise if the event
435 // handler spins the event loop all subsequent moves and releases get lost.
436 touchMouseId = p.id();
437 itemForTouchPointId[touchMouseId] = item;
438 QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event, item));
440 // Send a single press and see if that's accepted
441 if (!mouseGrabberItem)
443 item->grabTouchPoints(QVector<int>() << touchMouseId);
445 q->sendEvent(item, mousePress.data());
446 event->setAccepted(mousePress->isAccepted());
447 if (!mousePress->isAccepted()) {
449 if (itemForTouchPointId.value(p.id()) == item)
450 itemForTouchPointId.remove(p.id());
452 if (mouseGrabberItem == item)
456 if (doubleClick && mousePress->isAccepted()) {
457 touchMousePressTimestamp = 0;
458 QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item));
459 q->sendEvent(item, mouseDoubleClick.data());
460 event->setAccepted(mouseDoubleClick->isAccepted());
461 if (mouseDoubleClick->isAccepted()) {
467 // The event was accepted, we are done.
468 if (mousePress->isAccepted())
470 // The event was not accepted but touchMouseId was set.
471 if (touchMouseId != -1)
473 // try the next point
475 // Touch point was there before and moved
476 } else if (p.id() == touchMouseId) {
477 if (p.state() & Qt::TouchPointMoved) {
478 if (mouseGrabberItem) {
479 QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, mouseGrabberItem));
480 q->sendEvent(mouseGrabberItem, me.data());
481 event->setAccepted(me->isAccepted());
482 if (me->isAccepted()) {
483 itemForTouchPointId[p.id()] = mouseGrabberItem; // N.B. the mouseGrabberItem may be different after returning from sendEvent()
487 // no grabber, check if we care about mouse hover
488 // FIXME: this should only happen once, not recursively... I'll ignore it just ignore hover now.
489 // hover for touch???
490 QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, item));
491 if (lastMousePosition.isNull())
492 lastMousePosition = me->windowPos();
493 QPointF last = lastMousePosition;
494 lastMousePosition = me->windowPos();
496 bool accepted = me->isAccepted();
497 bool delivered = deliverHoverEvent(rootItem, me->windowPos(), last, me->modifiers(), accepted);
499 //take care of any exits
500 accepted = clearHover();
502 me->setAccepted(accepted);
505 } else if (p.state() & Qt::TouchPointReleased) {
506 // currently handled point was released
508 if (mouseGrabberItem) {
509 QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseButtonRelease, p, event, mouseGrabberItem));
510 q->sendEvent(mouseGrabberItem, me.data());
511 if (mouseGrabberItem) // might have ungrabbed due to event
512 mouseGrabberItem->ungrabMouse();
513 return me->isAccepted();
522 void QQuickWindowPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform)
524 QMatrix4x4 transformMatrix(transform);
525 for (int i=0; i<touchPoints.count(); i++) {
526 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
527 touchPoint.setRect(transform.mapRect(touchPoint.sceneRect()));
528 touchPoint.setStartPos(transform.map(touchPoint.startScenePos()));
529 touchPoint.setLastPos(transform.map(touchPoint.lastScenePos()));
530 touchPoint.setVelocity(transformMatrix.mapVector(touchPoint.velocity()).toVector2D());
536 Translates the data in \a touchEvent to this window. This method leaves the item local positions in
537 \a touchEvent untouched (these are filled in later).
539 void QQuickWindowPrivate::translateTouchEvent(QTouchEvent *touchEvent)
541 QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
542 for (int i = 0; i < touchPoints.count(); ++i) {
543 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
545 touchPoint.setScreenRect(touchPoint.sceneRect());
546 touchPoint.setStartScreenPos(touchPoint.startScenePos());
547 touchPoint.setLastScreenPos(touchPoint.lastScenePos());
549 touchPoint.setSceneRect(touchPoint.rect());
550 touchPoint.setStartScenePos(touchPoint.startPos());
551 touchPoint.setLastScenePos(touchPoint.lastPos());
554 lastMousePosition = touchPoint.pos().toPoint();
556 touchEvent->setTouchPoints(touchPoints);
559 void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
564 Q_ASSERT(scope || item == rootItem);
567 qWarning() << "QQuickWindowPrivate::setFocusInScope():";
568 qWarning() << " scope:" << (QObject *)scope;
570 qWarning() << " scopeSubFocusItem:" << (QObject *)QQuickItemPrivate::get(scope)->subFocusItem;
571 qWarning() << " item:" << (QObject *)item;
572 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
575 QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0;
576 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
578 QQuickItem *oldActiveFocusItem = 0;
579 QQuickItem *newActiveFocusItem = 0;
581 QVarLengthArray<QQuickItem *, 20> changed;
583 // Does this change the active focus?
584 if (item == rootItem || (scopePrivate->activeFocus && item->isEnabled())) {
585 oldActiveFocusItem = activeFocusItem;
586 newActiveFocusItem = item;
587 while (newActiveFocusItem->isFocusScope()
588 && newActiveFocusItem->scopedFocusItem()
589 && newActiveFocusItem->scopedFocusItem()->isEnabled()) {
590 newActiveFocusItem = newActiveFocusItem->scopedFocusItem();
593 if (oldActiveFocusItem) {
595 qApp->inputMethod()->commit();
599 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
600 q->sendEvent(oldActiveFocusItem, &event);
602 QQuickItem *afi = oldActiveFocusItem;
603 while (afi && afi != scope) {
604 if (QQuickItemPrivate::get(afi)->activeFocus) {
605 QQuickItemPrivate::get(afi)->activeFocus = false;
608 afi = afi->parentItem();
613 if (item != rootItem && !(options & DontChangeSubFocusItem)) {
614 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
615 if (oldSubFocusItem) {
616 QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
617 changed << oldSubFocusItem;
620 QQuickItemPrivate::get(item)->updateSubFocusItem(scope, true);
623 if (!(options & DontChangeFocusProperty)) {
624 // if (item != rootItem || QGuiApplication::focusWindow() == q) { // QTBUG-22415
625 itemPrivate->focus = true;
630 if (newActiveFocusItem && rootItem->hasFocus()) {
631 activeFocusItem = newActiveFocusItem;
633 QQuickItemPrivate::get(newActiveFocusItem)->activeFocus = true;
634 changed << newActiveFocusItem;
636 QQuickItem *afi = newActiveFocusItem->parentItem();
637 while (afi && afi != scope) {
638 if (afi->isFocusScope()) {
639 QQuickItemPrivate::get(afi)->activeFocus = true;
642 afi = afi->parentItem();
645 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
646 q->sendEvent(newActiveFocusItem, &event);
649 emit q->focusObjectChanged(activeFocusItem);
651 if (!changed.isEmpty())
652 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
655 void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
660 Q_ASSERT(scope || item == rootItem);
663 qWarning() << "QQuickWindowPrivate::clearFocusInScope():";
664 qWarning() << " scope:" << (QObject *)scope;
665 qWarning() << " item:" << (QObject *)item;
666 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
669 QQuickItemPrivate *scopePrivate = 0;
671 scopePrivate = QQuickItemPrivate::get(scope);
672 if ( !scopePrivate->subFocusItem )
673 return;//No focus, nothing to do.
676 QQuickItem *oldActiveFocusItem = 0;
677 QQuickItem *newActiveFocusItem = 0;
679 QVarLengthArray<QQuickItem *, 20> changed;
681 Q_ASSERT(item == rootItem || item == scopePrivate->subFocusItem);
683 // Does this change the active focus?
684 if (item == rootItem || scopePrivate->activeFocus) {
685 oldActiveFocusItem = activeFocusItem;
686 newActiveFocusItem = scope;
688 Q_ASSERT(oldActiveFocusItem);
691 qApp->inputMethod()->commit();
695 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
696 q->sendEvent(oldActiveFocusItem, &event);
698 QQuickItem *afi = oldActiveFocusItem;
699 while (afi && afi != scope) {
700 if (QQuickItemPrivate::get(afi)->activeFocus) {
701 QQuickItemPrivate::get(afi)->activeFocus = false;
704 afi = afi->parentItem();
708 if (item != rootItem && !(options & DontChangeSubFocusItem)) {
709 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
710 if (oldSubFocusItem && !(options & DontChangeFocusProperty)) {
711 QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
712 changed << oldSubFocusItem;
715 QQuickItemPrivate::get(item)->updateSubFocusItem(scope, false);
717 } else if (!(options & DontChangeFocusProperty)) {
718 QQuickItemPrivate::get(item)->focus = false;
722 if (newActiveFocusItem) {
723 Q_ASSERT(newActiveFocusItem == scope);
724 activeFocusItem = scope;
726 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
727 q->sendEvent(newActiveFocusItem, &event);
730 emit q->focusObjectChanged(activeFocusItem);
732 if (!changed.isEmpty())
733 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
736 void QQuickWindowPrivate::notifyFocusChangesRecur(QQuickItem **items, int remaining)
738 QQmlGuard<QQuickItem> item(*items);
741 notifyFocusChangesRecur(items + 1, remaining - 1);
744 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
746 if (itemPrivate->notifiedFocus != itemPrivate->focus) {
747 itemPrivate->notifiedFocus = itemPrivate->focus;
748 emit item->focusChanged(itemPrivate->focus);
751 if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) {
752 itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus;
753 itemPrivate->itemChange(QQuickItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus);
754 emit item->activeFocusChanged(itemPrivate->activeFocus);
759 void QQuickWindowPrivate::dirtyItem(QQuickItem *)
765 void QQuickWindowPrivate::cleanup(QSGNode *n)
769 Q_ASSERT(!cleanupNodeList.contains(n));
770 cleanupNodeList.append(n);
776 \qmlmodule QtQuick.Window 2
777 \title QML Module QtQuick.Window 2.0
778 \brief Contains types for window management
780 This QML module contains types for creating top-level windows and accessing screen information.
782 To use the types in this module, import the module with the following line:
785 import QtQuick.Window 2.0
790 \qmlclass Window QQuickWindow
791 \inqmlmodule QtQuick.Window 2
792 \ingroup qtquick-visual
793 \brief Creates a new top-level window
795 The Window object creates a new top-level window for a QtQuick scene. It automatically sets up the
796 window for use with QtQuick 2.0 graphical types.
798 To use this type, you will need to import the module with the following line:
800 import QtQuick.Window 2.0
803 Restricting this import will allow you to have a QML environment without access to window system features.
811 \brief The QQuickWindow class provides the window for displaying a graphical QML scene
813 QQuickWindow provides the graphical scene management needed to interact with and display
814 a scene of QQuickItems.
816 A QQuickWindow always has a single invisible root item. To add items to this window,
817 reparent the items to the root item or to an existing item in the scene.
819 For easily displaying a scene from a QML file, see \l{QQuickView}.
821 \section1 Scene Graph and Rendering
823 The QQuickWindow uses a scene graph on top of OpenGL to render. This scene graph is disconnected
824 from the QML scene and potentially lives in another thread, depending on the platform
825 implementation. Since the rendering scene graph lives independently from the QML scene, it can
826 also be completely released without affecting the state of the QML scene.
828 The sceneGraphInitialized() signal is emitted on the rendering thread before the QML scene is
829 rendered to the screen for the first time. If the rendering scene graph has been released
830 the signal will be emitted again before the next frame is rendered.
832 The rendering of each frame is broken down into the following
833 steps, in the given order:
837 \li The QQuickWindow::beforeSynchronizing() signal is emitted.
838 Applications can make direct connections (Qt::DirectConnection)
839 to this signal to do any preparation required before calls to
840 QQuickItem::updatePaintNode().
842 \li Synchronization of the QML state into the scene graph. This is
843 done by calling the QQuickItem::updatePaintNode() function on all
844 items that have changed since the previous frame. When a dedicated
845 rendering thread is used, the GUI thread is blocked during this
846 synchroniation. This is the only time the QML items and the nodes
847 in the scene graph interact.
849 \li The window to be rendered is made current using
850 QOpenGLContext::makeCurrent().
852 \li The QQuickWindow::beforeRendering() signal is
853 emitted. Applications can make direct connections
854 (Qt::DirectConnection) to this signal to use custom OpenGL calls
855 which will then stack visually beneath the QML scene.
857 \li Items that have specified QSGNode::UsesPreprocess, will have their
858 QSGNode::preprocess() function invoked.
860 \li The QQuickWindow is cleared according to what is specified
861 using QQuickWindow::setClearBeforeRendering() and
862 QQuickWindow::setClearColor().
864 \li The scene graph is rendered.
866 \li The QQuickWindow::afterRendering() signal is
867 emitted. Applications can make direct connections
868 (Qt::DirectConnection) to this signal to use custom OpenGL calls
869 which will then stack visually over the QML scene.
871 \li The rendered frame is swapped and QQuickWindow::frameSwapped()
876 All of the above happen on the rendering thread, when applicable.
878 While the scene graph is being rendered on the rendering thread, the GUI will process animations
879 for the next frame. This means that as long as users are not using scene graph API
880 directly, the added complexity of a rendering thread can be completely ignored.
882 When a QQuickWindow is programatically hidden with hide() or setVisible(false), it will
883 stop rendering and its scene graph and OpenGL context might be released. The
884 sceneGraphInvalidated() signal will be emitted when this happens.
886 \warning It is crucial that OpenGL operations and interaction with the scene graph happens
887 exclusively on the rendering thread, primarily during the updatePaintNode() phase.
889 \warning As signals related to rendering might be emitted from the rendering thread,
890 connections should be made using Qt::DirectConnection
893 \section2 Resource Management
895 QML will typically try to cache images, scene graph nodes, etc to improve performance, but in
896 some low-memory scenarios it might be required to aggressively release these resources. The
897 releaseResources() can be used to force clean up of certain resources. Calling releaseResources()
898 may result in the entire scene graph and its OpenGL context being deleted. The
899 sceneGraphInvalidated() signal will be emitted when this happens.
902 QQuickWindow::QQuickWindow(QWindow *parent)
903 : QWindow(*(new QQuickWindowPrivate), parent)
909 QQuickWindow::QQuickWindow(QQuickWindowPrivate &dd, QWindow *parent)
910 : QWindow(dd, parent)
916 QQuickWindow::~QQuickWindow()
920 d->windowManager->windowDestroyed(this);
922 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
923 delete d->incubationController; d->incubationController = 0;
925 delete d->rootItem; d->rootItem = 0;
931 This function tries to release redundant resources currently held by the QML scene.
933 Calling this function might result in the scene graph and the OpenGL context used
934 for rendering being released to release graphics memory. If this happens, the
935 sceneGraphInvalidated() signal will be called, allowing users to clean up their
936 own graphics resources. The setPersistentOpenGLContext() and setPersistentSceneGraph()
937 functions can be used to prevent this from happening, if handling the cleanup is
938 not feasible in the application, at the cost of higher memory usage.
940 \sa sceneGraphInvalidated(), setPersistentOpenGLContext(), setPersistentSceneGraph().
943 void QQuickWindow::releaseResources()
946 d->windowManager->releaseResources();
947 QQuickPixmap::purgeCache();
953 Controls whether the OpenGL context can be released as a part of a call to
956 The OpenGL context might still be released when the user makes an explicit
959 \sa setPersistentSceneGraph()
962 void QQuickWindow::setPersistentOpenGLContext(bool persistent)
965 d->persistentGLContext = persistent;
970 Returns whether the OpenGL context can be released as a part of a call to
974 bool QQuickWindow::isPersistentOpenGLContext() const
976 Q_D(const QQuickWindow);
977 return d->persistentGLContext;
983 Controls whether the scene graph nodes and resources can be released as a
984 part of a call to releaseResources().
986 The scene graph nodes and resources might still be released when the user
987 makes an explicit call to hide().
989 \sa setPersistentOpenGLContext()
992 void QQuickWindow::setPersistentSceneGraph(bool persistent)
995 d->persistentSceneGraph = persistent;
1001 Returns whether the scene graph nodes and resources can be released as a part
1002 of a call to releaseResources().
1005 bool QQuickWindow::isPersistentSceneGraph() const
1007 Q_D(const QQuickWindow);
1008 return d->persistentSceneGraph;
1016 Returns the invisible root item of the scene.
1018 A QQuickWindow always has a single invisible root item containing all of its content.
1019 To add items to this window, reparent the items to the contentItem or to an existing
1022 QQuickItem *QQuickWindow::contentItem() const
1024 Q_D(const QQuickWindow);
1030 Returns the item which currently has active focus.
1032 QQuickItem *QQuickWindow::activeFocusItem() const
1034 Q_D(const QQuickWindow);
1036 return d->activeFocusItem;
1043 QObject *QQuickWindow::focusObject() const
1045 Q_D(const QQuickWindow);
1047 if (d->activeFocusItem)
1048 return d->activeFocusItem;
1049 return const_cast<QQuickWindow*>(this);
1054 Returns the item which currently has the mouse grab.
1056 QQuickItem *QQuickWindow::mouseGrabberItem() const
1058 Q_D(const QQuickWindow);
1060 return d->mouseGrabberItem;
1065 \qmlproperty color QtQuick.Window2::Window::color
1067 The background color for the window.
1069 Setting this property is more efficient than using a separate Rectangle.
1072 bool QQuickWindowPrivate::clearHover()
1075 if (hoverItems.isEmpty())
1078 QPointF pos = q->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint());
1080 bool accepted = false;
1081 foreach (QQuickItem* item, hoverItems)
1082 accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), true) || accepted;
1088 bool QQuickWindow::event(QEvent *e)
1092 switch (e->type()) {
1094 case QEvent::TouchBegin:
1095 case QEvent::TouchUpdate:
1096 case QEvent::TouchEnd: {
1097 QTouchEvent *touch = static_cast<QTouchEvent*>(e);
1098 d->translateTouchEvent(touch);
1099 // return in order to avoid the QWindow::event below
1100 return d->deliverTouchEvent(touch);
1103 case QEvent::TouchCancel:
1104 // return in order to avoid the QWindow::event below
1105 return d->deliverTouchCancelEvent(static_cast<QTouchEvent*>(e));
1109 d->lastMousePosition = QPoint();
1111 #ifndef QT_NO_DRAGANDDROP
1112 case QEvent::DragEnter:
1113 case QEvent::DragLeave:
1114 case QEvent::DragMove:
1116 d->deliverDragEvent(&d->dragGrabber, e);
1119 case QEvent::WindowDeactivate:
1120 rootItem()->windowDeactivateEvent();
1122 case QEvent::FocusAboutToChange:
1123 if (d->activeFocusItem)
1124 qGuiApp->inputMethod()->commit();
1130 return QWindow::event(e);
1134 void QQuickWindow::keyPressEvent(QKeyEvent *e)
1138 if (d->activeFocusItem)
1139 sendEvent(d->activeFocusItem, e);
1143 void QQuickWindow::keyReleaseEvent(QKeyEvent *e)
1147 if (d->activeFocusItem)
1148 sendEvent(d->activeFocusItem, e);
1151 QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos)
1153 int caps = QGuiApplicationPrivate::mouseEventCaps(event);
1154 QVector2D velocity = QGuiApplicationPrivate::mouseEventVelocity(event);
1155 QMouseEvent *me = new QMouseEvent(event->type(),
1156 transformedLocalPos ? *transformedLocalPos : event->localPos(),
1157 event->windowPos(), event->screenPos(),
1158 event->button(), event->buttons(), event->modifiers());
1159 QGuiApplicationPrivate::setMouseEventCapsAndVelocity(me, caps, velocity);
1160 me->setTimestamp(event->timestamp());
1164 bool QQuickWindowPrivate::deliverInitialMousePressEvent(QQuickItem *item, QMouseEvent *event)
1168 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1170 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1171 QPointF p = item->mapFromScene(event->windowPos());
1172 if (!item->contains(p))
1176 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1177 for (int ii = children.count() - 1; ii >= 0; --ii) {
1178 QQuickItem *child = children.at(ii);
1179 if (!child->isVisible() || !child->isEnabled())
1181 if (deliverInitialMousePressEvent(child, event))
1185 if (itemPrivate->acceptedMouseButtons() & event->button()) {
1186 QPointF localPos = item->mapFromScene(event->windowPos());
1187 if (item->contains(localPos)) {
1188 QScopedPointer<QMouseEvent> me(cloneMouseEvent(event, &localPos));
1191 q->sendEvent(item, me.data());
1192 event->setAccepted(me->isAccepted());
1193 if (me->isAccepted())
1195 if (mouseGrabberItem && !event->buttons())
1196 mouseGrabberItem->ungrabMouse();
1203 bool QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event)
1207 lastMousePosition = event->windowPos();
1209 if (!mouseGrabberItem &&
1210 event->type() == QEvent::MouseButtonPress &&
1211 (event->buttons() & event->button()) == event->buttons()) {
1212 if (deliverInitialMousePressEvent(rootItem, event))
1216 return event->isAccepted();
1219 if (mouseGrabberItem) {
1220 QPointF localPos = mouseGrabberItem->mapFromScene(event->windowPos());
1221 QScopedPointer<QMouseEvent> me(cloneMouseEvent(event, &localPos));
1223 q->sendEvent(mouseGrabberItem, me.data());
1224 event->setAccepted(me->isAccepted());
1225 if (me->isAccepted())
1233 void QQuickWindow::mousePressEvent(QMouseEvent *event)
1237 qWarning() << "QQuickWindow::mousePressEvent()" << event->localPos() << event->button() << event->buttons();
1240 d->deliverMouseEvent(event);
1244 void QQuickWindow::mouseReleaseEvent(QMouseEvent *event)
1248 qWarning() << "QQuickWindow::mouseReleaseEvent()" << event->localPos() << event->button() << event->buttons();
1251 if (!d->mouseGrabberItem) {
1252 QWindow::mouseReleaseEvent(event);
1256 d->deliverMouseEvent(event);
1257 if (d->mouseGrabberItem && !event->buttons())
1258 d->mouseGrabberItem->ungrabMouse();
1262 void QQuickWindow::mouseDoubleClickEvent(QMouseEvent *event)
1266 qWarning() << "QQuickWindow::mouseDoubleClickEvent()" << event->localPos() << event->button() << event->buttons();
1269 if (!d->mouseGrabberItem && (event->buttons() & event->button()) == event->buttons()) {
1270 if (d->deliverInitialMousePressEvent(d->rootItem, event))
1277 d->deliverMouseEvent(event);
1280 bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
1281 const QPointF &scenePos, const QPointF &lastScenePos,
1282 Qt::KeyboardModifiers modifiers, bool accepted)
1285 const QTransform transform = QQuickItemPrivate::get(item)->windowToItemTransform();
1287 //create copy of event
1288 QHoverEvent hoverEvent(type, transform.map(scenePos), transform.map(lastScenePos), modifiers);
1289 hoverEvent.setAccepted(accepted);
1291 q->sendEvent(item, &hoverEvent);
1293 return hoverEvent.isAccepted();
1297 void QQuickWindow::mouseMoveEvent(QMouseEvent *event)
1301 qWarning() << "QQuickWindow::mouseMoveEvent()" << event->localPos() << event->button() << event->buttons();
1304 if (!d->mouseGrabberItem) {
1305 if (d->lastMousePosition.isNull())
1306 d->lastMousePosition = event->windowPos();
1307 QPointF last = d->lastMousePosition;
1308 d->lastMousePosition = event->windowPos();
1310 bool accepted = event->isAccepted();
1311 bool delivered = d->deliverHoverEvent(d->rootItem, event->windowPos(), last, event->modifiers(), accepted);
1313 //take care of any exits
1314 accepted = d->clearHover();
1316 event->setAccepted(accepted);
1320 d->deliverMouseEvent(event);
1323 bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
1324 Qt::KeyboardModifiers modifiers, bool &accepted)
1326 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1328 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1329 QPointF p = item->mapFromScene(scenePos);
1330 if (!item->contains(p))
1334 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1335 for (int ii = children.count() - 1; ii >= 0; --ii) {
1336 QQuickItem *child = children.at(ii);
1337 if (!child->isVisible() || !child->isEnabled())
1339 if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, accepted))
1343 if (itemPrivate->hoverEnabled) {
1344 QPointF p = item->mapFromScene(scenePos);
1345 if (item->contains(p)) {
1346 if (!hoverItems.isEmpty() && hoverItems[0] == item) {
1348 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1350 QList<QQuickItem *> itemsToHover;
1351 QQuickItem* parent = item;
1352 itemsToHover << item;
1353 while ((parent = parent->parentItem()))
1354 itemsToHover << parent;
1356 // Leaving from previous hovered items until we reach the item or one of its ancestors.
1357 while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems[0])) {
1358 sendHoverEvent(QEvent::HoverLeave, hoverItems[0], scenePos, lastScenePos, modifiers, accepted);
1359 hoverItems.removeFirst();
1362 if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item
1363 // ### Shouldn't we send moves for the parent items as well?
1364 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1366 // Enter items that are not entered yet.
1368 if (!hoverItems.isEmpty())
1369 startIdx = itemsToHover.indexOf(hoverItems[0]) - 1;
1371 startIdx = itemsToHover.count() - 1;
1373 for (int i = startIdx; i >= 0; i--) {
1374 QQuickItem *itemToHover = itemsToHover[i];
1375 if (QQuickItemPrivate::get(itemToHover)->hoverEnabled) {
1376 hoverItems.prepend(itemToHover);
1377 sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, accepted);
1389 bool QQuickWindowPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event)
1392 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1394 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1395 QPointF p = item->mapFromScene(event->posF());
1396 if (!item->contains(p))
1400 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1401 for (int ii = children.count() - 1; ii >= 0; --ii) {
1402 QQuickItem *child = children.at(ii);
1403 if (!child->isVisible() || !child->isEnabled())
1405 if (deliverWheelEvent(child, event))
1409 QPointF p = item->mapFromScene(event->posF());
1411 if (item->contains(p)) {
1412 QWheelEvent wheel(p, p, event->pixelDelta(), event->angleDelta(), event->delta(),
1413 event->orientation(), event->buttons(), event->modifiers());
1415 q->sendEvent(item, &wheel);
1416 if (wheel.isAccepted()) {
1425 #ifndef QT_NO_WHEELEVENT
1427 void QQuickWindow::wheelEvent(QWheelEvent *event)
1431 qWarning() << "QQuickWindow::wheelEvent()" << event->pixelDelta() << event->angleDelta();
1434 //if the actual wheel event was accepted, accept the compatability wheel event and return early
1435 if (d->lastWheelEventAccepted && event->angleDelta().isNull())
1439 d->deliverWheelEvent(d->rootItem, event);
1440 d->lastWheelEventAccepted = event->isAccepted();
1442 #endif // QT_NO_WHEELEVENT
1445 bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event)
1448 qWarning("touchCancelEvent");
1451 // A TouchCancel event will typically not contain any points.
1452 // Deliver it to all items that have active touches.
1453 QSet<QQuickItem *> cancelDelivered;
1454 foreach (QQuickItem *item, itemForTouchPointId) {
1455 if (cancelDelivered.contains(item))
1457 cancelDelivered.insert(item);
1458 q->sendEvent(item, event);
1461 if (mouseGrabberItem)
1462 mouseGrabberItem->ungrabMouse();
1463 // The next touch event can only be a TouchBegin so clean up.
1464 itemForTouchPointId.clear();
1468 // check what kind of touch we have (begin/update) and
1469 // call deliverTouchPoints to actually dispatch the points
1470 bool QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event)
1473 if (event->type() == QEvent::TouchBegin)
1474 qWarning() << "touchBeginEvent";
1475 else if (event->type() == QEvent::TouchUpdate)
1476 qWarning() << "touchUpdateEvent points";
1477 else if (event->type() == QEvent::TouchEnd)
1478 qWarning("touchEndEvent");
1481 // List of all items that received an event before
1482 // When we have TouchBegin this is and will stay empty
1483 QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > updatedPoints;
1485 // Figure out who accepted a touch point last and put it in updatedPoints
1486 // Add additional item to newPoints
1487 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
1488 QList<QTouchEvent::TouchPoint> newPoints;
1489 for (int i=0; i<touchPoints.count(); i++) {
1490 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
1491 if (touchPoint.state() == Qt::TouchPointPressed) {
1492 newPoints << touchPoint;
1494 // TouchPointStationary is relevant only to items which
1495 // are also receiving touch points with some other state.
1496 // But we have not yet decided which points go to which item,
1497 // so for now we must include all non-new points in updatedPoints.
1498 if (itemForTouchPointId.contains(touchPoint.id())) {
1499 QQuickItem *item = itemForTouchPointId.value(touchPoint.id());
1501 updatedPoints[item].append(touchPoint);
1506 // Deliver the event, but only if there is at least one new point
1507 // or some item accepted a point and should receive an update
1508 if (newPoints.count() > 0 || updatedPoints.count() > 0) {
1509 QSet<int> acceptedNewPoints;
1510 event->setAccepted(deliverTouchPoints(rootItem, event, newPoints, &acceptedNewPoints, &updatedPoints));
1514 // Remove released points from itemForTouchPointId
1515 if (event->touchPointStates() & Qt::TouchPointReleased) {
1516 for (int i=0; i<touchPoints.count(); i++) {
1517 if (touchPoints[i].state() == Qt::TouchPointReleased) {
1518 itemForTouchPointId.remove(touchPoints[i].id());
1519 if (touchPoints[i].id() == touchMouseId)
1525 if (event->type() == QEvent::TouchEnd) {
1526 Q_ASSERT(itemForTouchPointId.isEmpty());
1529 return event->isAccepted();
1532 // This function recurses and sends the events to the individual items
1533 bool QQuickWindowPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints, QSet<int> *acceptedNewPoints, QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *updatedPoints)
1535 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1537 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1538 for (int i=0; i<newPoints.count(); i++) {
1539 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1540 if (!item->contains(p))
1545 // Check if our children want the event (or parts of it)
1546 // This is the only point where touch event delivery recurses!
1547 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1548 for (int ii = children.count() - 1; ii >= 0; --ii) {
1549 QQuickItem *child = children.at(ii);
1550 if (!child->isEnabled() || !child->isVisible())
1552 if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints))
1556 // None of the children accepted the event, so check the given item itself.
1557 // First, construct matchingPoints as a list of TouchPoints which the
1558 // given item might be interested in. Any newly-pressed point which is
1559 // inside the item's bounds will be interesting, and also any updated point
1560 // which was already accepted by that item when it was first pressed.
1561 // (A point which was already accepted is effectively "grabbed" by the item.)
1563 // set of IDs of "interesting" new points
1564 QSet<int> matchingNewPoints;
1565 // set of points which this item has previously accepted, for starters
1566 QList<QTouchEvent::TouchPoint> matchingPoints = (*updatedPoints)[item];
1567 // now add the new points which are inside this item's bounds
1568 if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) {
1569 for (int i = 0; i < newPoints.count(); i++) {
1570 if (acceptedNewPoints->contains(newPoints[i].id()))
1572 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1573 if (item->contains(p)) {
1574 matchingNewPoints.insert(newPoints[i].id());
1575 matchingPoints << newPoints[i];
1579 // If there are no matching new points, and the existing points are all stationary,
1580 // there's no need to send an event to this item. This is required by a test in
1581 // tst_qquickwindow::touchEvent_basic:
1582 // a single stationary press on an item shouldn't cause an event
1583 if (matchingNewPoints.isEmpty()) {
1584 bool stationaryOnly = true;
1585 Q_FOREACH (QTouchEvent::TouchPoint tp, matchingPoints)
1586 if (tp.state() != Qt::TouchPointStationary)
1587 stationaryOnly = false;
1589 matchingPoints.clear();
1592 if (!matchingPoints.isEmpty()) {
1593 // Now we know this item might be interested in the event. Copy and send it, but
1594 // with only the subset of TouchPoints which are relevant to that item: that's matchingPoints.
1595 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1596 transformTouchPoints(matchingPoints, itemPrivate->windowToItemTransform());
1597 deliverMatchingPointsToItem(item, event, acceptedNewPoints, matchingNewPoints, matchingPoints);
1600 // record the fact that this item has been visited already
1601 updatedPoints->remove(item);
1603 // recursion is done only if ALL touch points have been delivered
1604 return (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty());
1607 // touchEventForItemBounds has no means to generate a touch event that contains
1608 // only the points that are relevant for this item. Thus the need for
1609 // matchingPoints to already be that set of interesting points.
1610 // They are all pre-transformed, too.
1611 bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints)
1613 QScopedPointer<QTouchEvent> touchEvent(touchEventWithPoints(*event, matchingPoints));
1614 touchEvent.data()->setTarget(item);
1615 bool touchEventAccepted = false;
1617 // First check whether the parent wants to be a filter,
1618 // and if the parent accepts the event we are done.
1619 if (sendFilteredTouchEvent(item->parentItem(), item, event)) {
1624 // Since it can change in sendEvent, update itemForTouchPointId now
1625 foreach (int id, matchingNewPoints)
1626 itemForTouchPointId[id] = item;
1628 // Deliver the touch event to the given item
1629 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1630 itemPrivate->deliverTouchEvent(touchEvent.data());
1631 touchEventAccepted = touchEvent->isAccepted();
1633 // If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it.
1634 if (!touchEventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) {
1636 event->setAccepted(translateTouchToMouse(item, event));
1637 if (event->isAccepted()) {
1638 touchEventAccepted = true;
1642 if (touchEventAccepted) {
1643 // If the touch was accepted (regardless by whom or in what form),
1644 // update acceptedNewPoints.
1645 foreach (int id, matchingNewPoints)
1646 acceptedNewPoints->insert(id);
1648 // But if the event was not accepted then we know this item
1649 // will not be interested in further updates for those touchpoint IDs either.
1650 foreach (int id, matchingNewPoints)
1651 if (itemForTouchPointId[id] == item)
1652 itemForTouchPointId.remove(id);
1655 return touchEventAccepted;
1658 QTouchEvent *QQuickWindowPrivate::touchEventForItemBounds(QQuickItem *target, const QTouchEvent &originalEvent)
1660 const QList<QTouchEvent::TouchPoint> &touchPoints = originalEvent.touchPoints();
1661 QList<QTouchEvent::TouchPoint> pointsInBounds;
1662 // if all points are stationary, the list of points should be empty to signal a no-op
1663 if (originalEvent.touchPointStates() != Qt::TouchPointStationary) {
1664 for (int i = 0; i < touchPoints.count(); ++i) {
1665 const QTouchEvent::TouchPoint &tp = touchPoints.at(i);
1666 if (tp.state() == Qt::TouchPointPressed) {
1667 QPointF p = target->mapFromScene(tp.scenePos());
1668 if (target->contains(p))
1669 pointsInBounds.append(tp);
1671 pointsInBounds.append(tp);
1674 transformTouchPoints(pointsInBounds, QQuickItemPrivate::get(target)->windowToItemTransform());
1677 QTouchEvent* touchEvent = touchEventWithPoints(originalEvent, pointsInBounds);
1678 touchEvent->setTarget(target);
1682 QTouchEvent *QQuickWindowPrivate::touchEventWithPoints(const QTouchEvent &event, const QList<QTouchEvent::TouchPoint> &newPoints)
1684 Qt::TouchPointStates eventStates;
1685 for (int i=0; i<newPoints.count(); i++)
1686 eventStates |= newPoints[i].state();
1687 // if all points have the same state, set the event type accordingly
1688 QEvent::Type eventType = event.type();
1689 switch (eventStates) {
1690 case Qt::TouchPointPressed:
1691 eventType = QEvent::TouchBegin;
1693 case Qt::TouchPointReleased:
1694 eventType = QEvent::TouchEnd;
1697 eventType = QEvent::TouchUpdate;
1701 QTouchEvent *touchEvent = new QTouchEvent(eventType);
1702 touchEvent->setWindow(event.window());
1703 touchEvent->setTarget(event.target());
1704 touchEvent->setDevice(event.device());
1705 touchEvent->setModifiers(event.modifiers());
1706 touchEvent->setTouchPoints(newPoints);
1707 touchEvent->setTouchPointStates(eventStates);
1708 touchEvent->setTimestamp(event.timestamp());
1709 touchEvent->accept();
1713 #ifndef QT_NO_DRAGANDDROP
1714 void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event)
1717 grabber->resetTarget();
1718 QQuickDragGrabber::iterator grabItem = grabber->begin();
1719 if (grabItem != grabber->end()) {
1720 Q_ASSERT(event->type() != QEvent::DragEnter);
1721 if (event->type() == QEvent::Drop) {
1722 QDropEvent *e = static_cast<QDropEvent *>(event);
1723 for (e->setAccepted(false); !e->isAccepted() && grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1724 QPointF p = (**grabItem)->mapFromScene(e->pos());
1725 QDropEvent translatedEvent(
1727 e->possibleActions(),
1730 e->keyboardModifiers());
1731 QQuickDropEventEx::copyActions(&translatedEvent, *e);
1732 q->sendEvent(**grabItem, &translatedEvent);
1733 e->setAccepted(translatedEvent.isAccepted());
1734 e->setDropAction(translatedEvent.dropAction());
1735 grabber->setTarget(**grabItem);
1738 if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave.
1739 QDragLeaveEvent leaveEvent;
1740 for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
1741 q->sendEvent(**grabItem, &leaveEvent);
1743 } else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1744 QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event);
1745 if (deliverDragEvent(grabber, **grabItem, moveEvent)) {
1746 moveEvent->setAccepted(true);
1747 for (++grabItem; grabItem != grabber->end();) {
1748 QPointF p = (**grabItem)->mapFromScene(moveEvent->pos());
1749 if ((**grabItem)->contains(p)) {
1750 QDragMoveEvent translatedEvent(
1752 moveEvent->possibleActions(),
1753 moveEvent->mimeData(),
1754 moveEvent->mouseButtons(),
1755 moveEvent->keyboardModifiers());
1756 QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent);
1757 q->sendEvent(**grabItem, &translatedEvent);
1760 QDragLeaveEvent leaveEvent;
1761 q->sendEvent(**grabItem, &leaveEvent);
1762 grabItem = grabber->release(grabItem);
1767 QDragLeaveEvent leaveEvent;
1768 q->sendEvent(**grabItem, &leaveEvent);
1772 if (event->type() == QEvent::DragEnter || event->type() == QEvent::DragMove) {
1773 QDragMoveEvent *e = static_cast<QDragMoveEvent *>(event);
1774 QDragEnterEvent enterEvent(
1776 e->possibleActions(),
1779 e->keyboardModifiers());
1780 QQuickDropEventEx::copyActions(&enterEvent, *e);
1781 event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent));
1785 bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event)
1788 bool accepted = false;
1789 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1790 if (!item->isVisible() || !item->isEnabled())
1793 QPointF p = item->mapFromScene(event->pos());
1794 if (item->contains(p)) {
1795 if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) {
1796 QDragMoveEvent translatedEvent(
1798 event->possibleActions(),
1800 event->mouseButtons(),
1801 event->keyboardModifiers(),
1803 QQuickDropEventEx::copyActions(&translatedEvent, *event);
1804 q->sendEvent(item, &translatedEvent);
1805 if (event->type() == QEvent::DragEnter) {
1806 if (translatedEvent.isAccepted()) {
1807 grabber->grab(item);
1814 } else if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1818 QDragEnterEvent enterEvent(
1820 event->possibleActions(),
1822 event->mouseButtons(),
1823 event->keyboardModifiers());
1824 QQuickDropEventEx::copyActions(&enterEvent, *event);
1825 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1826 for (int ii = children.count() - 1; ii >= 0; --ii) {
1827 if (deliverDragEvent(grabber, children.at(ii), &enterEvent))
1833 #endif // QT_NO_DRAGANDDROP
1835 bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event)
1840 QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target);
1841 if (targetPrivate->filtersChildMouseEvents) {
1842 QScopedPointer<QTouchEvent> targetEvent(touchEventForItemBounds(target, *event));
1843 if (!targetEvent->touchPoints().isEmpty()) {
1844 QVector<int> touchIds;
1845 for (int i = 0; i < event->touchPoints().size(); ++i)
1846 touchIds.append(event->touchPoints().at(i).id());
1847 if (target->childMouseEventFilter(item, targetEvent.data())) {
1848 target->grabTouchPoints(touchIds);
1849 if (mouseGrabberItem) {
1850 mouseGrabberItem->ungrabMouse();
1856 // Only offer a mouse event to the filter if we have one point
1857 if (targetEvent->touchPoints().count() == 1) {
1859 const QTouchEvent::TouchPoint &tp = targetEvent->touchPoints().first();
1860 switch (tp.state()) {
1861 case Qt::TouchPointPressed:
1862 t = QEvent::MouseButtonPress;
1864 case Qt::TouchPointReleased:
1865 t = QEvent::MouseButtonRelease;
1868 // move or stationary
1869 t = QEvent::MouseMove;
1873 // targetEvent is already transformed wrt local position, velocity, etc.
1874 QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, targetEvent->touchPoints().first(), event, item, false));
1875 if (target->childMouseEventFilter(item, mouseEvent.data())) {
1876 itemForTouchPointId[tp.id()] = target;
1877 touchMouseId = tp.id();
1878 target->grabMouse();
1885 return sendFilteredTouchEvent(target->parentItem(), item, event);
1888 bool QQuickWindowPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QEvent *event)
1893 QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target);
1894 if (targetPrivate->filtersChildMouseEvents)
1895 if (target->childMouseEventFilter(item, event))
1898 if (sendFilteredMouseEvent(target->parentItem(), item, event))
1904 bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event)
1906 QStyleHints *styleHints = qApp->styleHints();
1907 int caps = QGuiApplicationPrivate::mouseEventCaps(event);
1908 bool dragVelocityLimitAvailable = (caps & QTouchDevice::Velocity)
1909 && styleHints->startDragVelocity();
1910 bool overThreshold = qAbs(d) > styleHints->startDragDistance();
1911 if (dragVelocityLimitAvailable) {
1912 QVector2D velocityVec = QGuiApplicationPrivate::mouseEventVelocity(event);
1913 qreal velocity = axis == Qt::XAxis ? velocityVec.x() : velocityVec.y();
1914 overThreshold |= qAbs(velocity) > styleHints->startDragVelocity();
1916 return overThreshold;
1920 Propagates an event to a QQuickItem on the window
1922 bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e)
1927 qWarning("QQuickWindow::sendEvent: Cannot send event to a null item");
1933 switch (e->type()) {
1934 case QEvent::KeyPress:
1935 case QEvent::KeyRelease:
1937 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1938 while (!e->isAccepted() && (item = item->parentItem())) {
1940 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1943 case QEvent::FocusIn:
1944 case QEvent::FocusOut:
1945 QQuickItemPrivate::get(item)->deliverFocusEvent(static_cast<QFocusEvent *>(e));
1947 case QEvent::MouseButtonPress:
1948 case QEvent::MouseButtonRelease:
1949 case QEvent::MouseButtonDblClick:
1950 case QEvent::MouseMove:
1951 // XXX todo - should sendEvent be doing this? how does it relate to forwarded events?
1952 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
1953 // accept because qml items by default accept and have to explicitly opt out of accepting
1955 QQuickItemPrivate::get(item)->deliverMouseEvent(static_cast<QMouseEvent *>(e));
1958 case QEvent::UngrabMouse:
1959 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
1961 item->mouseUngrabEvent();
1965 QQuickItemPrivate::get(item)->deliverWheelEvent(static_cast<QWheelEvent *>(e));
1967 case QEvent::HoverEnter:
1968 case QEvent::HoverLeave:
1969 case QEvent::HoverMove:
1970 QQuickItemPrivate::get(item)->deliverHoverEvent(static_cast<QHoverEvent *>(e));
1972 case QEvent::TouchBegin:
1973 case QEvent::TouchUpdate:
1974 case QEvent::TouchEnd:
1975 d->sendFilteredTouchEvent(item->parentItem(), item, static_cast<QTouchEvent *>(e));
1977 case QEvent::TouchCancel:
1978 QQuickItemPrivate::get(item)->deliverTouchEvent(static_cast<QTouchEvent *>(e));
1980 #ifndef QT_NO_DRAGANDDROP
1981 case QEvent::DragEnter:
1982 case QEvent::DragMove:
1983 case QEvent::DragLeave:
1985 QQuickItemPrivate::get(item)->deliverDragEvent(e);
1995 void QQuickWindowPrivate::cleanupNodes()
1997 for (int ii = 0; ii < cleanupNodeList.count(); ++ii)
1998 delete cleanupNodeList.at(ii);
1999 cleanupNodeList.clear();
2002 void QQuickWindowPrivate::cleanupNodesOnShutdown(QQuickItem *item)
2004 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
2005 if (p->itemNodeInstance) {
2006 delete p->itemNodeInstance;
2007 p->itemNodeInstance = 0;
2009 if (p->extra.isAllocated()) {
2010 p->extra->opacityNode = 0;
2011 p->extra->clipNode = 0;
2012 p->extra->rootNode = 0;
2019 for (int ii = 0; ii < p->childItems.count(); ++ii)
2020 cleanupNodesOnShutdown(p->childItems.at(ii));
2023 // This must be called from the render thread, with the main thread frozen
2024 void QQuickWindowPrivate::cleanupNodesOnShutdown()
2028 cleanupNodesOnShutdown(rootItem);
2029 QSet<QQuickItem *>::const_iterator it = parentlessItems.begin();
2030 for (; it != parentlessItems.end(); ++it)
2031 cleanupNodesOnShutdown(*it);
2032 q->cleanupSceneGraph();
2035 void QQuickWindowPrivate::updateDirtyNodes()
2038 qWarning() << "QQuickWindowPrivate::updateDirtyNodes():";
2043 QQuickItem *updateList = dirtyItemList;
2045 if (updateList) QQuickItemPrivate::get(updateList)->prevDirtyItem = &updateList;
2047 while (updateList) {
2048 QQuickItem *item = updateList;
2049 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
2050 itemPriv->removeFromDirtyList();
2053 qWarning() << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString());
2055 updateDirtyNode(item);
2059 void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item)
2061 #ifdef QML_RUNTIME_TESTING
2062 bool didFlash = false;
2065 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
2066 quint32 dirty = itemPriv->dirtyAttributes;
2067 itemPriv->dirtyAttributes = 0;
2069 if ((dirty & QQuickItemPrivate::TransformUpdateMask) ||
2070 (dirty & QQuickItemPrivate::Size && itemPriv->origin() != QQuickItem::TopLeft &&
2071 (itemPriv->scale() != 1. || itemPriv->rotation() != 0.))) {
2075 if (itemPriv->x != 0. || itemPriv->y != 0.)
2076 matrix.translate(itemPriv->x, itemPriv->y);
2078 for (int ii = itemPriv->transforms.count() - 1; ii >= 0; --ii)
2079 itemPriv->transforms.at(ii)->applyTo(&matrix);
2081 if (itemPriv->scale() != 1. || itemPriv->rotation() != 0.) {
2082 QPointF origin = item->transformOriginPoint();
2083 matrix.translate(origin.x(), origin.y());
2084 if (itemPriv->scale() != 1.)
2085 matrix.scale(itemPriv->scale(), itemPriv->scale());
2086 if (itemPriv->rotation() != 0.)
2087 matrix.rotate(itemPriv->rotation(), 0, 0, 1);
2088 matrix.translate(-origin.x(), -origin.y());
2091 itemPriv->itemNode()->setMatrix(matrix);
2094 bool clipEffectivelyChanged = (dirty & (QQuickItemPrivate::Clip | QQuickItemPrivate::Window)) &&
2095 ((item->clip() == false) != (itemPriv->clipNode() == 0));
2096 int effectRefCount = itemPriv->extra.isAllocated()?itemPriv->extra->effectRefCount:0;
2097 bool effectRefEffectivelyChanged = (dirty & (QQuickItemPrivate::EffectReference | QQuickItemPrivate::Window)) &&
2098 ((effectRefCount == 0) != (itemPriv->rootNode() == 0));
2100 if (clipEffectivelyChanged) {
2101 QSGNode *parent = itemPriv->opacityNode() ? (QSGNode *) itemPriv->opacityNode() :
2102 (QSGNode *)itemPriv->itemNode();
2103 QSGNode *child = itemPriv->rootNode() ? (QSGNode *)itemPriv->rootNode() :
2104 (QSGNode *)itemPriv->groupNode;
2107 Q_ASSERT(itemPriv->clipNode() == 0);
2108 itemPriv->extra.value().clipNode = new QQuickDefaultClipNode(item->clipRect());
2109 itemPriv->clipNode()->update();
2112 parent->removeChildNode(child);
2113 parent->appendChildNode(itemPriv->clipNode());
2115 itemPriv->clipNode()->appendChildNode(child);
2118 Q_ASSERT(itemPriv->clipNode() != 0);
2119 parent->removeChildNode(itemPriv->clipNode());
2121 itemPriv->clipNode()->removeChildNode(child);
2122 delete itemPriv->clipNode();
2123 itemPriv->extra->clipNode = 0;
2125 parent->appendChildNode(child);
2129 if (dirty & QQuickItemPrivate::ChildrenUpdateMask)
2130 itemPriv->childContainerNode()->removeAllChildNodes();
2132 if (effectRefEffectivelyChanged) {
2133 QSGNode *parent = itemPriv->clipNode();
2135 parent = itemPriv->opacityNode();
2137 parent = itemPriv->itemNode();
2138 QSGNode *child = itemPriv->groupNode;
2140 if (itemPriv->extra.isAllocated() && itemPriv->extra->effectRefCount) {
2141 Q_ASSERT(itemPriv->rootNode() == 0);
2142 itemPriv->extra->rootNode = new QSGRootNode;
2145 parent->removeChildNode(child);
2146 parent->appendChildNode(itemPriv->rootNode());
2148 itemPriv->rootNode()->appendChildNode(child);
2150 Q_ASSERT(itemPriv->rootNode() != 0);
2151 parent->removeChildNode(itemPriv->rootNode());
2153 itemPriv->rootNode()->removeChildNode(child);
2154 delete itemPriv->rootNode();
2155 itemPriv->extra->rootNode = 0;
2157 parent->appendChildNode(child);
2161 if (dirty & QQuickItemPrivate::ChildrenUpdateMask) {
2162 QSGNode *groupNode = itemPriv->groupNode;
2164 groupNode->removeAllChildNodes();
2166 QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems();
2169 for (; ii < orderedChildren.count() && orderedChildren.at(ii)->z() < 0; ++ii) {
2170 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
2171 if (!childPrivate->explicitVisible &&
2172 (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount))
2174 if (childPrivate->itemNode()->parent())
2175 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
2177 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
2180 QSGNode *beforePaintNode = itemPriv->groupNode ? itemPriv->groupNode->lastChild() : 0;
2181 if (beforePaintNode || itemPriv->extra.isAllocated())
2182 itemPriv->extra.value().beforePaintNode = beforePaintNode;
2184 if (itemPriv->paintNode)
2185 itemPriv->childContainerNode()->appendChildNode(itemPriv->paintNode);
2187 for (; ii < orderedChildren.count(); ++ii) {
2188 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
2189 if (!childPrivate->explicitVisible &&
2190 (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount))
2192 if (childPrivate->itemNode()->parent())
2193 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
2195 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
2199 if ((dirty & QQuickItemPrivate::Size) && itemPriv->clipNode()) {
2200 itemPriv->clipNode()->setRect(item->clipRect());
2201 itemPriv->clipNode()->update();
2204 if (dirty & (QQuickItemPrivate::OpacityValue | QQuickItemPrivate::Visible
2205 | QQuickItemPrivate::HideReference | QQuickItemPrivate::Window))
2207 qreal opacity = itemPriv->explicitVisible && (!itemPriv->extra.isAllocated() || itemPriv->extra->hideRefCount == 0)
2208 ? itemPriv->opacity() : qreal(0);
2210 if (opacity != 1 && !itemPriv->opacityNode()) {
2211 itemPriv->extra.value().opacityNode = new QSGOpacityNode;
2213 QSGNode *parent = itemPriv->itemNode();
2214 QSGNode *child = itemPriv->clipNode();
2216 child = itemPriv->rootNode();
2218 child = itemPriv->groupNode;
2221 parent->removeChildNode(child);
2222 parent->appendChildNode(itemPriv->opacityNode());
2224 itemPriv->opacityNode()->appendChildNode(child);
2226 if (itemPriv->opacityNode())
2227 itemPriv->opacityNode()->setOpacity(opacity);
2230 if (dirty & QQuickItemPrivate::ContentUpdateMask) {
2232 if (itemPriv->flags & QQuickItem::ItemHasContents) {
2233 updatePaintNodeData.transformNode = itemPriv->itemNode();
2234 itemPriv->paintNode = item->updatePaintNode(itemPriv->paintNode, &updatePaintNodeData);
2236 Q_ASSERT(itemPriv->paintNode == 0 ||
2237 itemPriv->paintNode->parent() == 0 ||
2238 itemPriv->paintNode->parent() == itemPriv->childContainerNode());
2240 if (itemPriv->paintNode && itemPriv->paintNode->parent() == 0) {
2241 if (itemPriv->extra.isAllocated() && itemPriv->extra->beforePaintNode)
2242 itemPriv->childContainerNode()->insertChildNodeAfter(itemPriv->paintNode, itemPriv->extra->beforePaintNode);
2244 itemPriv->childContainerNode()->prependChildNode(itemPriv->paintNode);
2246 } else if (itemPriv->paintNode) {
2247 delete itemPriv->paintNode;
2248 itemPriv->paintNode = 0;
2253 // Check consistency.
2254 const QSGNode *nodeChain[] = {
2255 itemPriv->itemNodeInstance,
2256 itemPriv->opacityNode(),
2257 itemPriv->clipNode(),
2258 itemPriv->rootNode(),
2259 itemPriv->groupNode,
2260 itemPriv->paintNode,
2265 while (ip < 5 && nodeChain[ip] == 0)
2270 while (ic < 5 && nodeChain[ic] == 0)
2272 const QSGNode *parent = nodeChain[ip];
2273 const QSGNode *child = nodeChain[ic];
2275 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 0);
2277 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 1);
2278 Q_ASSERT(child->parent() == parent);
2279 bool containsChild = false;
2280 for (QSGNode *n = parent->firstChild(); n; n = n->nextSibling())
2281 containsChild |= (n == child);
2282 Q_ASSERT(containsChild);
2288 #ifdef QML_RUNTIME_TESTING
2289 if (itemPriv->sceneGraphContext()->isFlashModeEnabled()) {
2290 QSGFlashNode *flash = new QSGFlashNode();
2291 flash->setRect(item->boundingRect());
2292 itemPriv->childContainerNode()->appendChildNode(flash);
2303 void QQuickWindow::maybeUpdate()
2306 d->windowManager->maybeUpdate(this);
2309 void QQuickWindow::cleanupSceneGraph()
2316 delete d->renderer->rootNode();
2323 Returns the opengl context used for rendering.
2325 If the scene graph is not ready, this function will return 0.
2327 \sa sceneGraphInitialized(), sceneGraphInvalidated()
2330 QOpenGLContext *QQuickWindow::openglContext() const
2332 Q_D(const QQuickWindow);
2333 if (d->context->isReady())
2334 return d->context->glContext();
2340 \fn void QSGContext::sceneGraphInitialized()
2342 This signal is emitted when the scene graph has been initialized.
2344 This signal will be emitted from the scene graph rendering thread.
2350 \fn void QSGContext::sceneGraphInvalidated()
2352 This signal is emitted when the scene graph has been invalidated.
2354 This signal implies that the opengl rendering context used
2355 has been invalidated and all user resources tied to that context
2358 This signal will be emitted from the scene graph rendering thread.
2363 Sets the render target for this window to be \a fbo.
2365 The specified fbo must be created in the context of the window
2366 or one that shares with it.
2369 This function can only be called from the thread doing
2373 void QQuickWindow::setRenderTarget(QOpenGLFramebufferObject *fbo)
2376 if (d->context && d->context && QThread::currentThread() != d->context->thread()) {
2377 qWarning("QQuickWindow::setRenderThread: Cannot set render target from outside the rendering thread");
2381 d->renderTarget = fbo;
2383 d->renderTargetId = fbo->handle();
2384 d->renderTargetSize = fbo->size();
2386 d->renderTargetId = 0;
2387 d->renderTargetSize = QSize();
2394 void QQuickWindow::setRenderTarget(uint fboId, const QSize &size)
2397 if (d->context && d->context && QThread::currentThread() != d->context->thread()) {
2398 qWarning("QQuickWindow::setRenderThread: Cannot set render target from outside the rendering thread");
2402 d->renderTargetId = fboId;
2403 d->renderTargetSize = size;
2405 // Unset any previously set instance...
2406 d->renderTarget = 0;
2411 Returns the FBO id of the render target when set; otherwise returns 0.
2413 uint QQuickWindow::renderTargetId() const
2415 Q_D(const QQuickWindow);
2416 return d->renderTargetId;
2420 Returns the size of the currently set render target; otherwise returns an enpty size.
2422 QSize QQuickWindow::renderTargetSize() const
2424 Q_D(const QQuickWindow);
2425 return d->renderTargetSize;
2432 Returns the render target for this window.
2434 The default is to render to the surface of the window, in which
2435 case the render target is 0.
2437 QOpenGLFramebufferObject *QQuickWindow::renderTarget() const
2439 Q_D(const QQuickWindow);
2440 return d->renderTarget;
2445 Grabs the contents of the window and returns it as an image.
2447 This function might not work if the window is not visible.
2449 \warning Calling this function will cause performance problems.
2451 \warning This function can only be called from the GUI thread.
2453 QImage QQuickWindow::grabWindow()
2456 return d->windowManager->grab(this);
2460 Returns an incubation controller that splices incubation between frames
2461 for this window. QQuickView automatically installs this controller for you,
2462 otherwise you will need to install it yourself using \l{QQmlEngine::setIncubationController}
2464 The controller is owned by the window and will be destroyed when the window
2467 QQmlIncubationController *QQuickWindow::incubationController() const
2469 Q_D(const QQuickWindow);
2471 if (!d->incubationController)
2472 d->incubationController = new QQuickWindowIncubationController(const_cast<QQuickWindowPrivate *>(d));
2473 return d->incubationController;
2479 \enum QQuickWindow::CreateTextureOption
2481 The CreateTextureOption enums are used to customize a texture is wrapped.
2483 \value TextureHasAlphaChannel The texture has an alpha channel and should
2484 be drawn using blending.
2486 \value TextureHasMipmaps The texture has mipmaps and can be drawn with
2489 \value TextureOwnsGLTexture The texture object owns the texture id and
2490 will delete the GL texture when the texture object is deleted.
2494 \fn void QQuickWindow::beforeRendering()
2496 This signal is emitted before the scene starts rendering.
2498 Combined with the modes for clearing the background, this option
2499 can be used to paint using raw GL under QML content.
2501 The GL context used for rendering the scene graph will be bound
2504 \warning Since this signal is emitted from the scene graph rendering thread, the
2505 receiver should be on the scene graph thread or the connection should be Qt::DirectConnection.
2507 \warning Make very sure that a signal handler for beforeRendering leaves the GL
2508 context in the same state as it was when the signal handler was entered. Failing to
2509 do so can result in the scene not rendering properly.
2513 \fn void QQuickWindow::afterRendering()
2515 This signal is emitted after the scene has completed rendering, before swapbuffers is called.
2517 This signal can be used to paint using raw GL on top of QML content,
2518 or to do screen scraping of the current frame buffer.
2520 The GL context used for rendering the scene graph will be bound at this point.
2522 \warning Since this signal is emitted from the scene graph rendering thread, the
2523 receiver should be on the scene graph thread or the connection should be Qt::DirectConnection.
2525 \warning Make very sure that a signal handler for afterRendering() leaves the GL
2526 context in the same state as it was when the signal handler was entered. Failing to
2527 do so can result in the scene not rendering properly.
2533 Sets weither the scene graph rendering of QML should clear the color buffer
2534 before it starts rendering to \a enbled.
2536 By disabling clearing of the color buffer, it is possible to do GL painting
2537 under the scene graph.
2539 The color buffer is cleared by default.
2541 \sa beforeRendering()
2544 void QQuickWindow::setClearBeforeRendering(bool enabled)
2547 d->clearBeforeRendering = enabled;
2553 Returns weither clearing of the color buffer is done before rendering or not.
2556 bool QQuickWindow::clearBeforeRendering() const
2558 Q_D(const QQuickWindow);
2559 return d->clearBeforeRendering;
2565 Creates a new QSGTexture from the supplied \a image. If the image has an
2566 alpha channel, the corresponding texture will have an alpha channel.
2568 The caller of the function is responsible for deleting the returned texture.
2569 The actual GL texture will be deleted when the texture object is deleted.
2571 Depending on the underlying implementation of the scene graph, the returned
2572 texture may be part of an atlas. For code to be portable across implementations
2573 one should always use the texture coordinates returned from
2574 QSGTexture::normalizedTextureSubRect() when building geometry.
2576 \warning This function will return 0 if the scene graph has not yet been
2579 \warning The returned texture is not memory managed by the scene graph and
2580 must be explicitely deleted by the caller on the rendering thread.
2581 This is acheived by deleting the texture from a QSGNode destructor
2582 or by using deleteLater() in the case where the texture already has affinity
2583 to the rendering thread.
2585 This function can be called from any thread.
2587 \sa sceneGraphInitialized()
2590 QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const
2592 Q_D(const QQuickWindow);
2593 if (d->context && d->context->isReady())
2594 return d->context->createTexture(image);
2602 Creates a new QSGTexture object from an existing GL texture \a id.
2604 The caller of the function is responsible for deleting the returned texture.
2606 Use \a options to customize the texture attributes.
2608 \warning This function will return 0 if the scenegraph has not yet been
2611 \sa sceneGraphInitialized()
2613 QSGTexture *QQuickWindow::createTextureFromId(uint id, const QSize &size, CreateTextureOptions options) const
2615 Q_D(const QQuickWindow);
2616 if (d->context && d->context->isReady()) {
2617 QSGPlainTexture *texture = new QSGPlainTexture();
2618 texture->setTextureId(id);
2619 texture->setHasAlphaChannel(options & TextureHasAlphaChannel);
2620 texture->setHasMipmaps(options & TextureHasMipmaps);
2621 texture->setOwnsTexture(options & TextureOwnsGLTexture);
2622 texture->setTextureSize(size);
2630 Sets the color used to clear the opengl context to \a color.
2632 Setting the clear color has no effect when clearing is disabled.
2634 \sa setClearBeforeRendering()
2637 void QQuickWindow::setColor(const QColor &color)
2640 if (color == d->clearColor)
2643 d->clearColor = color;
2644 emit colorChanged(color);
2650 Returns the color used to clear the opengl context.
2653 QColor QQuickWindow::color() const
2655 return d_func()->clearColor;
2660 #include "moc_qquickwindow.cpp"