1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickwindow.h"
43 #include "qquickwindow_p.h"
45 #include "qquickitem.h"
46 #include "qquickitem_p.h"
47 #include "qquickevents_p_p.h"
49 #include <QtQuick/private/qsgrenderer_p.h>
50 #include <QtQuick/private/qsgtexture_p.h>
51 #include <QtQuick/private/qsgflashnode_p.h>
53 #include <private/qquickwindowmanager_p.h>
55 #include <private/qguiapplication_p.h>
56 #include <QtGui/QInputMethod>
58 #include <private/qabstractanimation_p.h>
60 #include <QtGui/qpainter.h>
61 #include <QtGui/qevent.h>
62 #include <QtGui/qmatrix4x4.h>
63 #include <QtGui/qstylehints.h>
64 #include <QtCore/qvarlengtharray.h>
65 #include <QtCore/qabstractanimation.h>
66 #include <QtQml/qqmlincubator.h>
68 #include <QtQuick/private/qquickpixmapcache_p.h>
70 #include <private/qqmlprofilerservice_p.h>
71 #include <private/qqmlmemoryprofiler_p.h>
75 void QQuickWindowPrivate::updateFocusItemTransform()
78 QQuickItem *focus = q->activeFocusItem();
79 if (focus && qApp->focusObject() == focus)
80 qApp->inputMethod()->setInputItemTransform(QQuickItemPrivate::get(focus)->itemToWindowTransform());
83 class QQuickWindowIncubationController : public QObject, public QQmlIncubationController
86 QQuickWindowIncubationController(QQuickWindowPrivate *window)
87 : m_window(window), m_eventSent(false) {}
90 virtual bool event(QEvent *e)
92 if (e->type() == QEvent::User) {
93 Q_ASSERT(m_eventSent);
94 volatile bool *amtp = m_window->windowManager->allowMainThreadProcessing();
95 while (incubatingObjectCount()) {
97 incubateWhile(amtp, 2);
100 QCoreApplication::processEvents();
105 return QObject::event(e);
108 virtual void incubatingObjectCountChanged(int count)
110 if (count && !m_eventSent) {
112 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
114 // If no animations are running, the renderer may be waiting
115 m_window->windowManager->wakeup();
119 QQuickWindowPrivate *m_window;
123 #ifndef QT_NO_ACCESSIBILITY
124 QAccessibleInterface *QQuickWindow::accessibleRoot() const
126 return QAccessible::queryAccessibleInterface(const_cast<QQuickWindow*>(this));
135 Prior to being added to a valid window items can set and clear focus with no
136 effect. Only once items are added to a window (by way of having a parent set that
137 already belongs to a window) do the focus rules apply. Focus goes back to
138 having no effect if an item is removed from a window.
140 When an item is moved into a new focus scope (either being added to a window
141 for the first time, or having its parent changed), if the focus scope already has
142 a scope focused item that takes precedence over the item being added. Otherwise,
143 the focus of the added tree is used. In the case of of a tree of items being
144 added to a window for the first time, which may have a conflicted focus state (two
145 or more items in one scope having focus set), the same rule is applied item by item -
146 thus the first item that has focus will get it (assuming the scope doesn't already
147 have a scope focused item), and the other items will have their focus cleared.
151 // #define FOCUS_DEBUG
152 // #define MOUSE_DEBUG
153 // #define TOUCH_DEBUG
154 // #define DIRTY_DEBUG
157 void printFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1);
160 QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData()
165 QQuickRootItem::QQuickRootItem()
170 void QQuickWindow::exposeEvent(QExposeEvent *)
173 d->windowManager->exposureChanged(this);
177 void QQuickWindow::resizeEvent(QResizeEvent *)
180 d->windowManager->resize(this, size());
184 void QQuickWindow::showEvent(QShowEvent *)
186 d_func()->windowManager->show(this);
190 void QQuickWindow::hideEvent(QHideEvent *)
192 d_func()->windowManager->hide(this);
196 void QQuickWindow::focusOutEvent(QFocusEvent *)
199 d->rootItem->setFocus(false);
203 void QQuickWindow::focusInEvent(QFocusEvent *)
206 d->rootItem->setFocus(true);
207 d->updateFocusItemTransform();
211 void QQuickWindowPrivate::polishItems()
213 int maxPolishCycles = 100000;
215 while (!itemsToPolish.isEmpty() && --maxPolishCycles > 0) {
216 QSet<QQuickItem *> itms = itemsToPolish;
217 itemsToPolish.clear();
219 for (QSet<QQuickItem *>::iterator it = itms.begin(); it != itms.end(); ++it) {
220 QQuickItem *item = *it;
221 QQuickItemPrivate::get(item)->polishScheduled = false;
222 item->updatePolish();
226 if (maxPolishCycles == 0)
227 qWarning("QQuickWindow: possible QQuickItem::polish() loop");
229 updateFocusItemTransform();
233 * This parameter enables that this window can be rendered without
234 * being shown on screen. This feature is very limited in what it supports.
236 * There needs to be another window actually showing that we can make current
237 * to get a surface to make current AND for this feature to be useful
238 * one needs to hook into beforeRender() and set the render tareget.
241 void QQuickWindowPrivate::setRenderWithoutShowing(bool render)
243 if (render == renderWithoutShowing)
247 renderWithoutShowing = render;
250 windowManager->show(q);
252 windowManager->hide(q);
257 * Schedules the window to render another frame.
259 * Calling QQuickWindow::update() differs from QQuickItem::update() in that
260 * it always triggers a repaint, regardless of changes in the underlying
261 * scene graph or not.
263 void QQuickWindow::update()
266 d->windowManager->update(this);
269 void forceUpdate(QQuickItem *item)
271 if (item->flags() & QQuickItem::ItemHasContents)
273 QQuickItemPrivate::get(item)->dirty(QQuickItemPrivate::ChildrenUpdateMask);
275 QList <QQuickItem *> items = item->childItems();
276 for (int i=0; i<items.size(); ++i)
277 forceUpdate(items.at(i));
280 void QQuickWindowPrivate::syncSceneGraph()
282 QML_MEMORY_SCOPE_STRING("SceneGraph");
285 emit q->beforeSynchronizing();
287 forceUpdate(rootItem);
289 QSGRootNode *rootNode = new QSGRootNode;
290 rootNode->appendChildNode(QQuickItemPrivate::get(rootItem)->itemNode());
291 renderer = context->createRenderer();
292 renderer->setRootNode(rootNode);
297 // Copy the current state of clearing from window into renderer.
298 renderer->setClearColor(clearColor);
299 QSGRenderer::ClearMode mode = QSGRenderer::ClearStencilBuffer | QSGRenderer::ClearDepthBuffer;
300 if (clearBeforeRendering)
301 mode |= QSGRenderer::ClearColorBuffer;
302 renderer->setClearMode(mode);
306 void QQuickWindowPrivate::renderSceneGraph(const QSize &size)
308 QML_MEMORY_SCOPE_STRING("SceneGraph");
310 emit q->beforeRendering();
312 renderer->setDeviceRect(QRect(QPoint(0, 0), size));
313 if (renderTargetId) {
314 fboId = renderTargetId;
315 renderer->setViewportRect(QRect(QPoint(0, 0), renderTargetSize));
317 renderer->setViewportRect(QRect(QPoint(0, 0), size));
319 renderer->setProjectionMatrixToDeviceRect();
321 context->renderNextFrame(renderer, fboId);
322 emit q->afterRendering();
325 QQuickWindowPrivate::QQuickWindowPrivate()
328 , mouseGrabberItem(0)
333 , touchMousePressTimestamp(0)
334 , renderWithoutShowing(false)
339 , clearColor(Qt::white)
340 , clearBeforeRendering(true)
341 , persistentGLContext(false)
342 , persistentSceneGraph(false)
343 , lastWheelEventAccepted(false)
346 , incubationController(0)
350 QQuickWindowPrivate::~QQuickWindowPrivate()
354 void QQuickWindowPrivate::init(QQuickWindow *c)
360 rootItem = new QQuickRootItem;
361 QQmlEngine::setObjectOwnership(rootItem, QQmlEngine::CppOwnership);
362 QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(rootItem);
363 rootItemPrivate->window = q;
364 rootItemPrivate->windowRefCount = 1;
365 rootItemPrivate->flags |= QQuickItem::ItemIsFocusScope;
367 // In the absence of a focus in event on some platforms assume the window will
368 // be activated immediately and set focus on the rootItem
369 // ### Remove when QTBUG-22415 is resolved.
370 //It is important that this call happens after the rootItem has a window..
371 rootItem->setFocus(true);
373 windowManager = QQuickWindowManager::instance();
374 context = windowManager->sceneGraphContext();
375 q->setSurfaceType(QWindow::OpenGLSurface);
376 q->setFormat(context->defaultSurfaceFormat());
378 QObject::connect(context, SIGNAL(initialized()), q, SIGNAL(sceneGraphInitialized()), Qt::DirectConnection);
379 QObject::connect(context, SIGNAL(invalidated()), q, SIGNAL(sceneGraphInvalidated()), Qt::DirectConnection);
380 QObject::connect(context, SIGNAL(invalidated()), q, SLOT(cleanupSceneGraph()), Qt::DirectConnection);
383 QQmlListProperty<QObject> QQuickWindowPrivate::data()
386 return QQuickItemPrivate::get(rootItem)->data();
389 void QQuickWindowPrivate::initRootItem()
392 q->connect(q, SIGNAL(widthChanged(int)),
393 rootItem, SLOT(setWidth(int)));
394 q->connect(q, SIGNAL(heightChanged(int)),
395 rootItem, SLOT(setHeight(int)));
396 rootItem->setWidth(q->width());
397 rootItem->setHeight(q->height());
400 static QMouseEvent *touchToMouseEvent(QEvent::Type type, const QTouchEvent::TouchPoint &p, QTouchEvent *event, QQuickItem *item, bool transformNeeded = true)
402 // The touch point local position and velocity are not yet transformed.
403 QMouseEvent *me = new QMouseEvent(type, transformNeeded ? item->mapFromScene(p.scenePos()) : p.pos(), p.scenePos(), p.screenPos(),
404 Qt::LeftButton, Qt::LeftButton, event->modifiers());
405 me->setAccepted(true);
406 me->setTimestamp(event->timestamp());
407 QVector2D transformedVelocity = p.velocity();
408 if (transformNeeded) {
409 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
410 QMatrix4x4 transformMatrix(itemPrivate->windowToItemTransform());
411 transformedVelocity = transformMatrix.mapVector(p.velocity()).toVector2D();
413 QGuiApplicationPrivate::setMouseEventCapsAndVelocity(me, event->device()->capabilities(), transformedVelocity);
417 bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *event)
420 // For each point, check if it is accepted, if not, try the next point.
421 // Any of the fingers can become the mouse one.
422 // This can happen because a mouse area might not accept an event at some point but another.
423 for (int i = 0; i < event->touchPoints().count(); ++i) {
424 const QTouchEvent::TouchPoint &p = event->touchPoints().at(i);
426 if (touchMouseId == -1 && p.state() & Qt::TouchPointPressed) {
427 QPointF pos = item->mapFromScene(p.scenePos());
429 // probably redundant, we check bounds in the calling function (matchingNewPoints)
430 if (!item->contains(pos))
433 bool doubleClick = event->timestamp() - touchMousePressTimestamp
434 < static_cast<ulong>(qApp->styleHints()->mouseDoubleClickInterval());
435 touchMousePressTimestamp = event->timestamp();
436 // Store the id already here and restore it to -1 if the event does not get
437 // accepted. Cannot defer setting the new value because otherwise if the event
438 // handler spins the event loop all subsequent moves and releases get lost.
439 touchMouseId = p.id();
440 itemForTouchPointId[touchMouseId] = item;
441 QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event, item));
443 // Send a single press and see if that's accepted
444 if (!mouseGrabberItem)
446 item->grabTouchPoints(QVector<int>() << touchMouseId);
448 q->sendEvent(item, mousePress.data());
449 event->setAccepted(mousePress->isAccepted());
450 if (!mousePress->isAccepted()) {
452 if (itemForTouchPointId.value(p.id()) == item)
453 itemForTouchPointId.remove(p.id());
455 if (mouseGrabberItem == item)
459 if (doubleClick && mousePress->isAccepted()) {
460 touchMousePressTimestamp = 0;
461 QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item));
462 q->sendEvent(item, mouseDoubleClick.data());
463 event->setAccepted(mouseDoubleClick->isAccepted());
464 if (mouseDoubleClick->isAccepted()) {
470 // The event was accepted, we are done.
471 if (mousePress->isAccepted())
473 // The event was not accepted but touchMouseId was set.
474 if (touchMouseId != -1)
476 // try the next point
478 // Touch point was there before and moved
479 } else if (p.id() == touchMouseId) {
480 if (p.state() & Qt::TouchPointMoved) {
481 if (mouseGrabberItem) {
482 QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, mouseGrabberItem));
483 q->sendEvent(mouseGrabberItem, me.data());
484 event->setAccepted(me->isAccepted());
485 if (me->isAccepted()) {
486 itemForTouchPointId[p.id()] = mouseGrabberItem; // N.B. the mouseGrabberItem may be different after returning from sendEvent()
490 // no grabber, check if we care about mouse hover
491 // FIXME: this should only happen once, not recursively... I'll ignore it just ignore hover now.
492 // hover for touch???
493 QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, item));
494 if (lastMousePosition.isNull())
495 lastMousePosition = me->windowPos();
496 QPointF last = lastMousePosition;
497 lastMousePosition = me->windowPos();
499 bool accepted = me->isAccepted();
500 bool delivered = deliverHoverEvent(rootItem, me->windowPos(), last, me->modifiers(), accepted);
502 //take care of any exits
503 accepted = clearHover();
505 me->setAccepted(accepted);
508 } else if (p.state() & Qt::TouchPointReleased) {
509 // currently handled point was released
511 if (mouseGrabberItem) {
512 QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseButtonRelease, p, event, mouseGrabberItem));
513 q->sendEvent(mouseGrabberItem, me.data());
514 if (mouseGrabberItem) // might have ungrabbed due to event
515 mouseGrabberItem->ungrabMouse();
516 return me->isAccepted();
525 void QQuickWindowPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform)
527 QMatrix4x4 transformMatrix(transform);
528 for (int i=0; i<touchPoints.count(); i++) {
529 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
530 touchPoint.setRect(transform.mapRect(touchPoint.sceneRect()));
531 touchPoint.setStartPos(transform.map(touchPoint.startScenePos()));
532 touchPoint.setLastPos(transform.map(touchPoint.lastScenePos()));
533 touchPoint.setVelocity(transformMatrix.mapVector(touchPoint.velocity()).toVector2D());
539 Translates the data in \a touchEvent to this window. This method leaves the item local positions in
540 \a touchEvent untouched (these are filled in later).
542 void QQuickWindowPrivate::translateTouchEvent(QTouchEvent *touchEvent)
544 QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
545 for (int i = 0; i < touchPoints.count(); ++i) {
546 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
548 touchPoint.setScreenRect(touchPoint.sceneRect());
549 touchPoint.setStartScreenPos(touchPoint.startScenePos());
550 touchPoint.setLastScreenPos(touchPoint.lastScenePos());
552 touchPoint.setSceneRect(touchPoint.rect());
553 touchPoint.setStartScenePos(touchPoint.startPos());
554 touchPoint.setLastScenePos(touchPoint.lastPos());
557 lastMousePosition = touchPoint.pos().toPoint();
559 touchEvent->setTouchPoints(touchPoints);
562 void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
567 Q_ASSERT(scope || item == rootItem);
570 qWarning() << "QQuickWindowPrivate::setFocusInScope():";
571 qWarning() << " scope:" << (QObject *)scope;
573 qWarning() << " scopeSubFocusItem:" << (QObject *)QQuickItemPrivate::get(scope)->subFocusItem;
574 qWarning() << " item:" << (QObject *)item;
575 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
578 QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0;
579 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
581 QQuickItem *oldActiveFocusItem = 0;
582 QQuickItem *newActiveFocusItem = 0;
584 QVarLengthArray<QQuickItem *, 20> changed;
586 // Does this change the active focus?
587 if (item == rootItem || (scopePrivate->activeFocus && item->isEnabled())) {
588 oldActiveFocusItem = activeFocusItem;
589 newActiveFocusItem = item;
590 while (newActiveFocusItem->isFocusScope()
591 && newActiveFocusItem->scopedFocusItem()
592 && newActiveFocusItem->scopedFocusItem()->isEnabled()) {
593 newActiveFocusItem = newActiveFocusItem->scopedFocusItem();
596 if (oldActiveFocusItem) {
598 qApp->inputMethod()->commit();
602 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
603 q->sendEvent(oldActiveFocusItem, &event);
605 QQuickItem *afi = oldActiveFocusItem;
606 while (afi && afi != scope) {
607 if (QQuickItemPrivate::get(afi)->activeFocus) {
608 QQuickItemPrivate::get(afi)->activeFocus = false;
611 afi = afi->parentItem();
616 if (item != rootItem && !(options & DontChangeSubFocusItem)) {
617 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
618 if (oldSubFocusItem) {
619 QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
620 changed << oldSubFocusItem;
623 QQuickItemPrivate::get(item)->updateSubFocusItem(scope, true);
626 if (!(options & DontChangeFocusProperty)) {
627 // if (item != rootItem || QGuiApplication::focusWindow() == q) { // QTBUG-22415
628 itemPrivate->focus = true;
633 if (newActiveFocusItem && rootItem->hasFocus()) {
634 activeFocusItem = newActiveFocusItem;
636 QQuickItemPrivate::get(newActiveFocusItem)->activeFocus = true;
637 changed << newActiveFocusItem;
639 QQuickItem *afi = newActiveFocusItem->parentItem();
640 while (afi && afi != scope) {
641 if (afi->isFocusScope()) {
642 QQuickItemPrivate::get(afi)->activeFocus = true;
645 afi = afi->parentItem();
648 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
649 q->sendEvent(newActiveFocusItem, &event);
652 emit q->focusObjectChanged(activeFocusItem);
654 if (!changed.isEmpty())
655 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
658 void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
663 Q_ASSERT(scope || item == rootItem);
666 qWarning() << "QQuickWindowPrivate::clearFocusInScope():";
667 qWarning() << " scope:" << (QObject *)scope;
668 qWarning() << " item:" << (QObject *)item;
669 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
672 QQuickItemPrivate *scopePrivate = 0;
674 scopePrivate = QQuickItemPrivate::get(scope);
675 if ( !scopePrivate->subFocusItem )
676 return;//No focus, nothing to do.
679 QQuickItem *oldActiveFocusItem = 0;
680 QQuickItem *newActiveFocusItem = 0;
682 QVarLengthArray<QQuickItem *, 20> changed;
684 Q_ASSERT(item == rootItem || item == scopePrivate->subFocusItem);
686 // Does this change the active focus?
687 if (item == rootItem || scopePrivate->activeFocus) {
688 oldActiveFocusItem = activeFocusItem;
689 newActiveFocusItem = scope;
691 Q_ASSERT(oldActiveFocusItem);
694 qApp->inputMethod()->commit();
698 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
699 q->sendEvent(oldActiveFocusItem, &event);
701 QQuickItem *afi = oldActiveFocusItem;
702 while (afi && afi != scope) {
703 if (QQuickItemPrivate::get(afi)->activeFocus) {
704 QQuickItemPrivate::get(afi)->activeFocus = false;
707 afi = afi->parentItem();
711 if (item != rootItem && !(options & DontChangeSubFocusItem)) {
712 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
713 if (oldSubFocusItem && !(options & DontChangeFocusProperty)) {
714 QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
715 changed << oldSubFocusItem;
718 QQuickItemPrivate::get(item)->updateSubFocusItem(scope, false);
720 } else if (!(options & DontChangeFocusProperty)) {
721 QQuickItemPrivate::get(item)->focus = false;
725 if (newActiveFocusItem) {
726 Q_ASSERT(newActiveFocusItem == scope);
727 activeFocusItem = scope;
729 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
730 q->sendEvent(newActiveFocusItem, &event);
733 emit q->focusObjectChanged(activeFocusItem);
735 if (!changed.isEmpty())
736 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
739 void QQuickWindowPrivate::notifyFocusChangesRecur(QQuickItem **items, int remaining)
741 QQmlGuard<QQuickItem> item(*items);
744 notifyFocusChangesRecur(items + 1, remaining - 1);
747 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
749 if (itemPrivate->notifiedFocus != itemPrivate->focus) {
750 itemPrivate->notifiedFocus = itemPrivate->focus;
751 emit item->focusChanged(itemPrivate->focus);
754 if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) {
755 itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus;
756 itemPrivate->itemChange(QQuickItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus);
757 emit item->activeFocusChanged(itemPrivate->activeFocus);
762 void QQuickWindowPrivate::dirtyItem(QQuickItem *)
768 void QQuickWindowPrivate::cleanup(QSGNode *n)
772 Q_ASSERT(!cleanupNodeList.contains(n));
773 cleanupNodeList.append(n);
779 \qmlmodule QtQuick.Window 2
780 \title QML Module QtQuick.Window 2.0
781 \brief Contains types for window management
783 This QML module contains types for creating top-level windows and accessing screen information.
785 To use the types in this module, import the module with the following line:
788 import QtQuick.Window 2.0
794 \instantiates QQuickWindow
795 \inqmlmodule QtQuick.Window 2
796 \ingroup qtquick-visual
797 \brief Creates a new top-level window
799 The Window object creates a new top-level window for a QtQuick scene. It automatically sets up the
800 window for use with QtQuick 2.0 graphical types.
802 To use this type, you will need to import the module with the following line:
804 import QtQuick.Window 2.0
807 Restricting this import will allow you to have a QML environment without access to window system features.
815 \brief The QQuickWindow class provides the window for displaying a graphical QML scene
817 QQuickWindow provides the graphical scene management needed to interact with and display
818 a scene of QQuickItems.
820 A QQuickWindow always has a single invisible root item. To add items to this window,
821 reparent the items to the root item or to an existing item in the scene.
823 For easily displaying a scene from a QML file, see \l{QQuickView}.
825 \section1 Scene Graph and Rendering
827 The QQuickWindow uses a scene graph on top of OpenGL to render. This scene graph is disconnected
828 from the QML scene and potentially lives in another thread, depending on the platform
829 implementation. Since the rendering scene graph lives independently from the QML scene, it can
830 also be completely released without affecting the state of the QML scene.
832 The sceneGraphInitialized() signal is emitted on the rendering thread before the QML scene is
833 rendered to the screen for the first time. If the rendering scene graph has been released
834 the signal will be emitted again before the next frame is rendered.
836 The rendering of each frame is broken down into the following
837 steps, in the given order:
841 \li The QQuickWindow::beforeSynchronizing() signal is emitted.
842 Applications can make direct connections (Qt::DirectConnection)
843 to this signal to do any preparation required before calls to
844 QQuickItem::updatePaintNode().
846 \li Synchronization of the QML state into the scene graph. This is
847 done by calling the QQuickItem::updatePaintNode() function on all
848 items that have changed since the previous frame. When a dedicated
849 rendering thread is used, the GUI thread is blocked during this
850 synchroniation. This is the only time the QML items and the nodes
851 in the scene graph interact.
853 \li The window to be rendered is made current using
854 QOpenGLContext::makeCurrent().
856 \li The QQuickWindow::beforeRendering() signal is
857 emitted. Applications can make direct connections
858 (Qt::DirectConnection) to this signal to use custom OpenGL calls
859 which will then stack visually beneath the QML scene.
861 \li Items that have specified QSGNode::UsesPreprocess, will have their
862 QSGNode::preprocess() function invoked.
864 \li The QQuickWindow is cleared according to what is specified
865 using QQuickWindow::setClearBeforeRendering() and
866 QQuickWindow::setClearColor().
868 \li The scene graph is rendered.
870 \li The QQuickWindow::afterRendering() signal is
871 emitted. Applications can make direct connections
872 (Qt::DirectConnection) to this signal to use custom OpenGL calls
873 which will then stack visually over the QML scene.
875 \li The rendered frame is swapped and QQuickWindow::frameSwapped()
880 All of the above happen on the rendering thread, when applicable.
882 While the scene graph is being rendered on the rendering thread, the GUI will process animations
883 for the next frame. This means that as long as users are not using scene graph API
884 directly, the added complexity of a rendering thread can be completely ignored.
886 When a QQuickWindow is programatically hidden with hide() or setVisible(false), it will
887 stop rendering and its scene graph and OpenGL context might be released. The
888 sceneGraphInvalidated() signal will be emitted when this happens.
890 \warning It is crucial that OpenGL operations and interaction with the scene graph happens
891 exclusively on the rendering thread, primarily during the updatePaintNode() phase.
893 \warning As signals related to rendering might be emitted from the rendering thread,
894 connections should be made using Qt::DirectConnection
897 \section2 Resource Management
899 QML will typically try to cache images, scene graph nodes, etc to improve performance, but in
900 some low-memory scenarios it might be required to aggressively release these resources. The
901 releaseResources() can be used to force clean up of certain resources. Calling releaseResources()
902 may result in the entire scene graph and its OpenGL context being deleted. The
903 sceneGraphInvalidated() signal will be emitted when this happens.
906 QQuickWindow::QQuickWindow(QWindow *parent)
907 : QWindow(*(new QQuickWindowPrivate), parent)
913 QQuickWindow::QQuickWindow(QQuickWindowPrivate &dd, QWindow *parent)
914 : QWindow(dd, parent)
920 QQuickWindow::~QQuickWindow()
924 d->windowManager->windowDestroyed(this);
926 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
927 delete d->incubationController; d->incubationController = 0;
929 delete d->rootItem; d->rootItem = 0;
935 This function tries to release redundant resources currently held by the QML scene.
937 Calling this function might result in the scene graph and the OpenGL context used
938 for rendering being released to release graphics memory. If this happens, the
939 sceneGraphInvalidated() signal will be called, allowing users to clean up their
940 own graphics resources. The setPersistentOpenGLContext() and setPersistentSceneGraph()
941 functions can be used to prevent this from happening, if handling the cleanup is
942 not feasible in the application, at the cost of higher memory usage.
944 \sa sceneGraphInvalidated(), setPersistentOpenGLContext(), setPersistentSceneGraph().
947 void QQuickWindow::releaseResources()
950 d->windowManager->releaseResources();
951 QQuickPixmap::purgeCache();
957 Controls whether the OpenGL context can be released as a part of a call to
960 The OpenGL context might still be released when the user makes an explicit
963 \sa setPersistentSceneGraph()
966 void QQuickWindow::setPersistentOpenGLContext(bool persistent)
969 d->persistentGLContext = persistent;
974 Returns whether the OpenGL context can be released as a part of a call to
978 bool QQuickWindow::isPersistentOpenGLContext() const
980 Q_D(const QQuickWindow);
981 return d->persistentGLContext;
987 Controls whether the scene graph nodes and resources can be released as a
988 part of a call to releaseResources().
990 The scene graph nodes and resources might still be released when the user
991 makes an explicit call to hide().
993 \sa setPersistentOpenGLContext()
996 void QQuickWindow::setPersistentSceneGraph(bool persistent)
999 d->persistentSceneGraph = persistent;
1005 Returns whether the scene graph nodes and resources can be released as a part
1006 of a call to releaseResources().
1009 bool QQuickWindow::isPersistentSceneGraph() const
1011 Q_D(const QQuickWindow);
1012 return d->persistentSceneGraph;
1020 Returns the invisible root item of the scene.
1022 A QQuickWindow always has a single invisible root item containing all of its content.
1023 To add items to this window, reparent the items to the contentItem or to an existing
1026 QQuickItem *QQuickWindow::contentItem() const
1028 Q_D(const QQuickWindow);
1034 Returns the item which currently has active focus.
1036 QQuickItem *QQuickWindow::activeFocusItem() const
1038 Q_D(const QQuickWindow);
1040 return d->activeFocusItem;
1047 QObject *QQuickWindow::focusObject() const
1049 Q_D(const QQuickWindow);
1051 if (d->activeFocusItem)
1052 return d->activeFocusItem;
1053 return const_cast<QQuickWindow*>(this);
1058 Returns the item which currently has the mouse grab.
1060 QQuickItem *QQuickWindow::mouseGrabberItem() const
1062 Q_D(const QQuickWindow);
1064 return d->mouseGrabberItem;
1069 \qmlproperty color QtQuick.Window2::Window::color
1071 The background color for the window.
1073 Setting this property is more efficient than using a separate Rectangle.
1076 bool QQuickWindowPrivate::clearHover()
1079 if (hoverItems.isEmpty())
1082 QPointF pos = q->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint());
1084 bool accepted = false;
1085 foreach (QQuickItem* item, hoverItems)
1086 accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), true) || accepted;
1092 bool QQuickWindow::event(QEvent *e)
1096 switch (e->type()) {
1098 case QEvent::TouchBegin:
1099 case QEvent::TouchUpdate:
1100 case QEvent::TouchEnd: {
1101 QTouchEvent *touch = static_cast<QTouchEvent*>(e);
1102 d->translateTouchEvent(touch);
1103 // return in order to avoid the QWindow::event below
1104 return d->deliverTouchEvent(touch);
1107 case QEvent::TouchCancel:
1108 // return in order to avoid the QWindow::event below
1109 return d->deliverTouchCancelEvent(static_cast<QTouchEvent*>(e));
1113 d->lastMousePosition = QPoint();
1115 #ifndef QT_NO_DRAGANDDROP
1116 case QEvent::DragEnter:
1117 case QEvent::DragLeave:
1118 case QEvent::DragMove:
1120 d->deliverDragEvent(&d->dragGrabber, e);
1123 case QEvent::WindowDeactivate:
1124 rootItem()->windowDeactivateEvent();
1126 case QEvent::FocusAboutToChange:
1127 if (d->activeFocusItem)
1128 qGuiApp->inputMethod()->commit();
1134 return QWindow::event(e);
1138 void QQuickWindow::keyPressEvent(QKeyEvent *e)
1142 if (d->activeFocusItem)
1143 sendEvent(d->activeFocusItem, e);
1147 void QQuickWindow::keyReleaseEvent(QKeyEvent *e)
1151 if (d->activeFocusItem)
1152 sendEvent(d->activeFocusItem, e);
1155 QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos)
1157 int caps = QGuiApplicationPrivate::mouseEventCaps(event);
1158 QVector2D velocity = QGuiApplicationPrivate::mouseEventVelocity(event);
1159 QMouseEvent *me = new QMouseEvent(event->type(),
1160 transformedLocalPos ? *transformedLocalPos : event->localPos(),
1161 event->windowPos(), event->screenPos(),
1162 event->button(), event->buttons(), event->modifiers());
1163 QGuiApplicationPrivate::setMouseEventCapsAndVelocity(me, caps, velocity);
1164 me->setTimestamp(event->timestamp());
1168 bool QQuickWindowPrivate::deliverInitialMousePressEvent(QQuickItem *item, QMouseEvent *event)
1172 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1174 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1175 QPointF p = item->mapFromScene(event->windowPos());
1176 if (!item->contains(p))
1180 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1181 for (int ii = children.count() - 1; ii >= 0; --ii) {
1182 QQuickItem *child = children.at(ii);
1183 if (!child->isVisible() || !child->isEnabled())
1185 if (deliverInitialMousePressEvent(child, event))
1189 if (itemPrivate->acceptedMouseButtons() & event->button()) {
1190 QPointF localPos = item->mapFromScene(event->windowPos());
1191 if (item->contains(localPos)) {
1192 QScopedPointer<QMouseEvent> me(cloneMouseEvent(event, &localPos));
1195 q->sendEvent(item, me.data());
1196 event->setAccepted(me->isAccepted());
1197 if (me->isAccepted())
1199 if (mouseGrabberItem && !event->buttons())
1200 mouseGrabberItem->ungrabMouse();
1207 bool QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event)
1211 lastMousePosition = event->windowPos();
1213 if (!mouseGrabberItem &&
1214 event->type() == QEvent::MouseButtonPress &&
1215 (event->buttons() & event->button()) == event->buttons()) {
1216 if (deliverInitialMousePressEvent(rootItem, event))
1220 return event->isAccepted();
1223 if (mouseGrabberItem) {
1224 QPointF localPos = mouseGrabberItem->mapFromScene(event->windowPos());
1225 QScopedPointer<QMouseEvent> me(cloneMouseEvent(event, &localPos));
1227 q->sendEvent(mouseGrabberItem, me.data());
1228 event->setAccepted(me->isAccepted());
1229 if (me->isAccepted())
1237 void QQuickWindow::mousePressEvent(QMouseEvent *event)
1241 qWarning() << "QQuickWindow::mousePressEvent()" << event->localPos() << event->button() << event->buttons();
1244 d->deliverMouseEvent(event);
1248 void QQuickWindow::mouseReleaseEvent(QMouseEvent *event)
1252 qWarning() << "QQuickWindow::mouseReleaseEvent()" << event->localPos() << event->button() << event->buttons();
1255 if (!d->mouseGrabberItem) {
1256 QWindow::mouseReleaseEvent(event);
1260 d->deliverMouseEvent(event);
1261 if (d->mouseGrabberItem && !event->buttons())
1262 d->mouseGrabberItem->ungrabMouse();
1266 void QQuickWindow::mouseDoubleClickEvent(QMouseEvent *event)
1270 qWarning() << "QQuickWindow::mouseDoubleClickEvent()" << event->localPos() << event->button() << event->buttons();
1273 if (!d->mouseGrabberItem && (event->buttons() & event->button()) == event->buttons()) {
1274 if (d->deliverInitialMousePressEvent(d->rootItem, event))
1281 d->deliverMouseEvent(event);
1284 bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
1285 const QPointF &scenePos, const QPointF &lastScenePos,
1286 Qt::KeyboardModifiers modifiers, bool accepted)
1289 const QTransform transform = QQuickItemPrivate::get(item)->windowToItemTransform();
1291 //create copy of event
1292 QHoverEvent hoverEvent(type, transform.map(scenePos), transform.map(lastScenePos), modifiers);
1293 hoverEvent.setAccepted(accepted);
1295 q->sendEvent(item, &hoverEvent);
1297 return hoverEvent.isAccepted();
1301 void QQuickWindow::mouseMoveEvent(QMouseEvent *event)
1305 qWarning() << "QQuickWindow::mouseMoveEvent()" << event->localPos() << event->button() << event->buttons();
1308 #ifndef QT_NO_CURSOR
1309 d->updateCursor(event->windowPos());
1312 if (!d->mouseGrabberItem) {
1313 if (d->lastMousePosition.isNull())
1314 d->lastMousePosition = event->windowPos();
1315 QPointF last = d->lastMousePosition;
1316 d->lastMousePosition = event->windowPos();
1318 bool accepted = event->isAccepted();
1319 bool delivered = d->deliverHoverEvent(d->rootItem, event->windowPos(), last, event->modifiers(), accepted);
1321 //take care of any exits
1322 accepted = d->clearHover();
1324 event->setAccepted(accepted);
1328 d->deliverMouseEvent(event);
1331 bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
1332 Qt::KeyboardModifiers modifiers, bool &accepted)
1334 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1336 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1337 QPointF p = item->mapFromScene(scenePos);
1338 if (!item->contains(p))
1342 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1343 for (int ii = children.count() - 1; ii >= 0; --ii) {
1344 QQuickItem *child = children.at(ii);
1345 if (!child->isVisible() || !child->isEnabled())
1347 if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, accepted))
1351 if (itemPrivate->hoverEnabled) {
1352 QPointF p = item->mapFromScene(scenePos);
1353 if (item->contains(p)) {
1354 if (!hoverItems.isEmpty() && hoverItems[0] == item) {
1356 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1358 QList<QQuickItem *> itemsToHover;
1359 QQuickItem* parent = item;
1360 itemsToHover << item;
1361 while ((parent = parent->parentItem()))
1362 itemsToHover << parent;
1364 // Leaving from previous hovered items until we reach the item or one of its ancestors.
1365 while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems[0])) {
1366 sendHoverEvent(QEvent::HoverLeave, hoverItems[0], scenePos, lastScenePos, modifiers, accepted);
1367 hoverItems.removeFirst();
1370 if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item
1371 // ### Shouldn't we send moves for the parent items as well?
1372 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1374 // Enter items that are not entered yet.
1376 if (!hoverItems.isEmpty())
1377 startIdx = itemsToHover.indexOf(hoverItems[0]) - 1;
1379 startIdx = itemsToHover.count() - 1;
1381 for (int i = startIdx; i >= 0; i--) {
1382 QQuickItem *itemToHover = itemsToHover[i];
1383 if (QQuickItemPrivate::get(itemToHover)->hoverEnabled) {
1384 hoverItems.prepend(itemToHover);
1385 sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, accepted);
1397 bool QQuickWindowPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event)
1400 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1402 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1403 QPointF p = item->mapFromScene(event->posF());
1404 if (!item->contains(p))
1408 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1409 for (int ii = children.count() - 1; ii >= 0; --ii) {
1410 QQuickItem *child = children.at(ii);
1411 if (!child->isVisible() || !child->isEnabled())
1413 if (deliverWheelEvent(child, event))
1417 QPointF p = item->mapFromScene(event->posF());
1419 if (item->contains(p)) {
1420 QWheelEvent wheel(p, p, event->pixelDelta(), event->angleDelta(), event->delta(),
1421 event->orientation(), event->buttons(), event->modifiers());
1423 q->sendEvent(item, &wheel);
1424 if (wheel.isAccepted()) {
1433 #ifndef QT_NO_WHEELEVENT
1435 void QQuickWindow::wheelEvent(QWheelEvent *event)
1439 qWarning() << "QQuickWindow::wheelEvent()" << event->pixelDelta() << event->angleDelta();
1442 //if the actual wheel event was accepted, accept the compatability wheel event and return early
1443 if (d->lastWheelEventAccepted && event->angleDelta().isNull())
1447 d->deliverWheelEvent(d->rootItem, event);
1448 d->lastWheelEventAccepted = event->isAccepted();
1450 #endif // QT_NO_WHEELEVENT
1453 bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event)
1456 qWarning("touchCancelEvent");
1459 // A TouchCancel event will typically not contain any points.
1460 // Deliver it to all items that have active touches.
1461 QSet<QQuickItem *> cancelDelivered;
1462 foreach (QQuickItem *item, itemForTouchPointId) {
1463 if (cancelDelivered.contains(item))
1465 cancelDelivered.insert(item);
1466 q->sendEvent(item, event);
1469 if (mouseGrabberItem)
1470 mouseGrabberItem->ungrabMouse();
1471 // The next touch event can only be a TouchBegin so clean up.
1472 itemForTouchPointId.clear();
1476 // check what kind of touch we have (begin/update) and
1477 // call deliverTouchPoints to actually dispatch the points
1478 bool QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event)
1481 if (event->type() == QEvent::TouchBegin)
1482 qWarning() << "touchBeginEvent";
1483 else if (event->type() == QEvent::TouchUpdate)
1484 qWarning() << "touchUpdateEvent points";
1485 else if (event->type() == QEvent::TouchEnd)
1486 qWarning("touchEndEvent");
1489 // List of all items that received an event before
1490 // When we have TouchBegin this is and will stay empty
1491 QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > updatedPoints;
1493 // Figure out who accepted a touch point last and put it in updatedPoints
1494 // Add additional item to newPoints
1495 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
1496 QList<QTouchEvent::TouchPoint> newPoints;
1497 for (int i=0; i<touchPoints.count(); i++) {
1498 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
1499 if (touchPoint.state() == Qt::TouchPointPressed) {
1500 newPoints << touchPoint;
1502 // TouchPointStationary is relevant only to items which
1503 // are also receiving touch points with some other state.
1504 // But we have not yet decided which points go to which item,
1505 // so for now we must include all non-new points in updatedPoints.
1506 if (itemForTouchPointId.contains(touchPoint.id())) {
1507 QQuickItem *item = itemForTouchPointId.value(touchPoint.id());
1509 updatedPoints[item].append(touchPoint);
1514 // Deliver the event, but only if there is at least one new point
1515 // or some item accepted a point and should receive an update
1516 if (newPoints.count() > 0 || updatedPoints.count() > 0) {
1517 QSet<int> acceptedNewPoints;
1518 event->setAccepted(deliverTouchPoints(rootItem, event, newPoints, &acceptedNewPoints, &updatedPoints));
1522 // Remove released points from itemForTouchPointId
1523 if (event->touchPointStates() & Qt::TouchPointReleased) {
1524 for (int i=0; i<touchPoints.count(); i++) {
1525 if (touchPoints[i].state() == Qt::TouchPointReleased) {
1526 itemForTouchPointId.remove(touchPoints[i].id());
1527 if (touchPoints[i].id() == touchMouseId)
1533 if (event->type() == QEvent::TouchEnd) {
1534 Q_ASSERT(itemForTouchPointId.isEmpty());
1537 return event->isAccepted();
1540 // This function recurses and sends the events to the individual items
1541 bool QQuickWindowPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints, QSet<int> *acceptedNewPoints, QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *updatedPoints)
1543 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1545 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1546 for (int i=0; i<newPoints.count(); i++) {
1547 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1548 if (!item->contains(p))
1553 // Check if our children want the event (or parts of it)
1554 // This is the only point where touch event delivery recurses!
1555 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1556 for (int ii = children.count() - 1; ii >= 0; --ii) {
1557 QQuickItem *child = children.at(ii);
1558 if (!child->isEnabled() || !child->isVisible())
1560 if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints))
1564 // None of the children accepted the event, so check the given item itself.
1565 // First, construct matchingPoints as a list of TouchPoints which the
1566 // given item might be interested in. Any newly-pressed point which is
1567 // inside the item's bounds will be interesting, and also any updated point
1568 // which was already accepted by that item when it was first pressed.
1569 // (A point which was already accepted is effectively "grabbed" by the item.)
1571 // set of IDs of "interesting" new points
1572 QSet<int> matchingNewPoints;
1573 // set of points which this item has previously accepted, for starters
1574 QList<QTouchEvent::TouchPoint> matchingPoints = (*updatedPoints)[item];
1575 // now add the new points which are inside this item's bounds
1576 if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) {
1577 for (int i = 0; i < newPoints.count(); i++) {
1578 if (acceptedNewPoints->contains(newPoints[i].id()))
1580 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1581 if (item->contains(p)) {
1582 matchingNewPoints.insert(newPoints[i].id());
1583 matchingPoints << newPoints[i];
1587 // If there are no matching new points, and the existing points are all stationary,
1588 // there's no need to send an event to this item. This is required by a test in
1589 // tst_qquickwindow::touchEvent_basic:
1590 // a single stationary press on an item shouldn't cause an event
1591 if (matchingNewPoints.isEmpty()) {
1592 bool stationaryOnly = true;
1593 Q_FOREACH (QTouchEvent::TouchPoint tp, matchingPoints)
1594 if (tp.state() != Qt::TouchPointStationary)
1595 stationaryOnly = false;
1597 matchingPoints.clear();
1600 if (!matchingPoints.isEmpty()) {
1601 // Now we know this item might be interested in the event. Copy and send it, but
1602 // with only the subset of TouchPoints which are relevant to that item: that's matchingPoints.
1603 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1604 transformTouchPoints(matchingPoints, itemPrivate->windowToItemTransform());
1605 deliverMatchingPointsToItem(item, event, acceptedNewPoints, matchingNewPoints, matchingPoints);
1608 // record the fact that this item has been visited already
1609 updatedPoints->remove(item);
1611 // recursion is done only if ALL touch points have been delivered
1612 return (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty());
1615 // touchEventForItemBounds has no means to generate a touch event that contains
1616 // only the points that are relevant for this item. Thus the need for
1617 // matchingPoints to already be that set of interesting points.
1618 // They are all pre-transformed, too.
1619 bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints)
1621 QScopedPointer<QTouchEvent> touchEvent(touchEventWithPoints(*event, matchingPoints));
1622 touchEvent.data()->setTarget(item);
1623 bool touchEventAccepted = false;
1625 // First check whether the parent wants to be a filter,
1626 // and if the parent accepts the event we are done.
1627 if (sendFilteredTouchEvent(item->parentItem(), item, event)) {
1632 // Since it can change in sendEvent, update itemForTouchPointId now
1633 foreach (int id, matchingNewPoints)
1634 itemForTouchPointId[id] = item;
1636 // Deliver the touch event to the given item
1637 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1638 itemPrivate->deliverTouchEvent(touchEvent.data());
1639 touchEventAccepted = touchEvent->isAccepted();
1641 // If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it.
1642 if (!touchEventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) {
1644 event->setAccepted(translateTouchToMouse(item, event));
1645 if (event->isAccepted()) {
1646 touchEventAccepted = true;
1650 if (touchEventAccepted) {
1651 // If the touch was accepted (regardless by whom or in what form),
1652 // update acceptedNewPoints.
1653 foreach (int id, matchingNewPoints)
1654 acceptedNewPoints->insert(id);
1656 // But if the event was not accepted then we know this item
1657 // will not be interested in further updates for those touchpoint IDs either.
1658 foreach (int id, matchingNewPoints)
1659 if (itemForTouchPointId[id] == item)
1660 itemForTouchPointId.remove(id);
1663 return touchEventAccepted;
1666 QTouchEvent *QQuickWindowPrivate::touchEventForItemBounds(QQuickItem *target, const QTouchEvent &originalEvent)
1668 const QList<QTouchEvent::TouchPoint> &touchPoints = originalEvent.touchPoints();
1669 QList<QTouchEvent::TouchPoint> pointsInBounds;
1670 // if all points are stationary, the list of points should be empty to signal a no-op
1671 if (originalEvent.touchPointStates() != Qt::TouchPointStationary) {
1672 for (int i = 0; i < touchPoints.count(); ++i) {
1673 const QTouchEvent::TouchPoint &tp = touchPoints.at(i);
1674 if (tp.state() == Qt::TouchPointPressed) {
1675 QPointF p = target->mapFromScene(tp.scenePos());
1676 if (target->contains(p))
1677 pointsInBounds.append(tp);
1679 pointsInBounds.append(tp);
1682 transformTouchPoints(pointsInBounds, QQuickItemPrivate::get(target)->windowToItemTransform());
1685 QTouchEvent* touchEvent = touchEventWithPoints(originalEvent, pointsInBounds);
1686 touchEvent->setTarget(target);
1690 QTouchEvent *QQuickWindowPrivate::touchEventWithPoints(const QTouchEvent &event, const QList<QTouchEvent::TouchPoint> &newPoints)
1692 Qt::TouchPointStates eventStates;
1693 for (int i=0; i<newPoints.count(); i++)
1694 eventStates |= newPoints[i].state();
1695 // if all points have the same state, set the event type accordingly
1696 QEvent::Type eventType = event.type();
1697 switch (eventStates) {
1698 case Qt::TouchPointPressed:
1699 eventType = QEvent::TouchBegin;
1701 case Qt::TouchPointReleased:
1702 eventType = QEvent::TouchEnd;
1705 eventType = QEvent::TouchUpdate;
1709 QTouchEvent *touchEvent = new QTouchEvent(eventType);
1710 touchEvent->setWindow(event.window());
1711 touchEvent->setTarget(event.target());
1712 touchEvent->setDevice(event.device());
1713 touchEvent->setModifiers(event.modifiers());
1714 touchEvent->setTouchPoints(newPoints);
1715 touchEvent->setTouchPointStates(eventStates);
1716 touchEvent->setTimestamp(event.timestamp());
1717 touchEvent->accept();
1721 #ifndef QT_NO_DRAGANDDROP
1722 void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event)
1725 grabber->resetTarget();
1726 QQuickDragGrabber::iterator grabItem = grabber->begin();
1727 if (grabItem != grabber->end()) {
1728 Q_ASSERT(event->type() != QEvent::DragEnter);
1729 if (event->type() == QEvent::Drop) {
1730 QDropEvent *e = static_cast<QDropEvent *>(event);
1731 for (e->setAccepted(false); !e->isAccepted() && grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1732 QPointF p = (**grabItem)->mapFromScene(e->pos());
1733 QDropEvent translatedEvent(
1735 e->possibleActions(),
1738 e->keyboardModifiers());
1739 QQuickDropEventEx::copyActions(&translatedEvent, *e);
1740 q->sendEvent(**grabItem, &translatedEvent);
1741 e->setAccepted(translatedEvent.isAccepted());
1742 e->setDropAction(translatedEvent.dropAction());
1743 grabber->setTarget(**grabItem);
1746 if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave.
1747 QDragLeaveEvent leaveEvent;
1748 for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
1749 q->sendEvent(**grabItem, &leaveEvent);
1751 } else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1752 QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event);
1753 if (deliverDragEvent(grabber, **grabItem, moveEvent)) {
1754 moveEvent->setAccepted(true);
1755 for (++grabItem; grabItem != grabber->end();) {
1756 QPointF p = (**grabItem)->mapFromScene(moveEvent->pos());
1757 if ((**grabItem)->contains(p)) {
1758 QDragMoveEvent translatedEvent(
1760 moveEvent->possibleActions(),
1761 moveEvent->mimeData(),
1762 moveEvent->mouseButtons(),
1763 moveEvent->keyboardModifiers());
1764 QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent);
1765 q->sendEvent(**grabItem, &translatedEvent);
1768 QDragLeaveEvent leaveEvent;
1769 q->sendEvent(**grabItem, &leaveEvent);
1770 grabItem = grabber->release(grabItem);
1775 QDragLeaveEvent leaveEvent;
1776 q->sendEvent(**grabItem, &leaveEvent);
1780 if (event->type() == QEvent::DragEnter || event->type() == QEvent::DragMove) {
1781 QDragMoveEvent *e = static_cast<QDragMoveEvent *>(event);
1782 QDragEnterEvent enterEvent(
1784 e->possibleActions(),
1787 e->keyboardModifiers());
1788 QQuickDropEventEx::copyActions(&enterEvent, *e);
1789 event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent));
1793 bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event)
1796 bool accepted = false;
1797 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1798 if (!item->isVisible() || !item->isEnabled())
1801 QPointF p = item->mapFromScene(event->pos());
1802 if (item->contains(p)) {
1803 if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) {
1804 QDragMoveEvent translatedEvent(
1806 event->possibleActions(),
1808 event->mouseButtons(),
1809 event->keyboardModifiers(),
1811 QQuickDropEventEx::copyActions(&translatedEvent, *event);
1812 q->sendEvent(item, &translatedEvent);
1813 if (event->type() == QEvent::DragEnter) {
1814 if (translatedEvent.isAccepted()) {
1815 grabber->grab(item);
1822 } else if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1826 QDragEnterEvent enterEvent(
1828 event->possibleActions(),
1830 event->mouseButtons(),
1831 event->keyboardModifiers());
1832 QQuickDropEventEx::copyActions(&enterEvent, *event);
1833 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1834 for (int ii = children.count() - 1; ii >= 0; --ii) {
1835 if (deliverDragEvent(grabber, children.at(ii), &enterEvent))
1841 #endif // QT_NO_DRAGANDDROP
1843 #ifndef QT_NO_CURSOR
1844 void QQuickWindowPrivate::updateCursor(const QPointF &scenePos)
1848 QQuickItem *oldCursorItem = cursorItem;
1849 cursorItem = findCursorItem(rootItem, scenePos);
1851 if (cursorItem != oldCursorItem) {
1853 q->setCursor(cursorItem->cursor());
1859 QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF &scenePos)
1861 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1862 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1863 QPointF p = item->mapFromScene(scenePos);
1864 if (!item->contains(p))
1868 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1869 for (int ii = children.count() - 1; ii >= 0; --ii) {
1870 QQuickItem *child = children.at(ii);
1871 if (!child->isVisible() || !child->isEnabled())
1873 if (QQuickItem *cursorItem = findCursorItem(child, scenePos))
1877 if (itemPrivate->hasCursor) {
1878 QPointF p = item->mapFromScene(scenePos);
1879 if (item->contains(p))
1886 bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event)
1891 QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target);
1892 if (targetPrivate->filtersChildMouseEvents) {
1893 QScopedPointer<QTouchEvent> targetEvent(touchEventForItemBounds(target, *event));
1894 if (!targetEvent->touchPoints().isEmpty()) {
1895 QVector<int> touchIds;
1896 for (int i = 0; i < event->touchPoints().size(); ++i)
1897 touchIds.append(event->touchPoints().at(i).id());
1898 if (target->childMouseEventFilter(item, targetEvent.data())) {
1899 target->grabTouchPoints(touchIds);
1900 if (mouseGrabberItem) {
1901 mouseGrabberItem->ungrabMouse();
1907 // Only offer a mouse event to the filter if we have one point
1908 if (targetEvent->touchPoints().count() == 1) {
1910 const QTouchEvent::TouchPoint &tp = targetEvent->touchPoints().first();
1911 switch (tp.state()) {
1912 case Qt::TouchPointPressed:
1913 t = QEvent::MouseButtonPress;
1915 case Qt::TouchPointReleased:
1916 t = QEvent::MouseButtonRelease;
1919 // move or stationary
1920 t = QEvent::MouseMove;
1924 // targetEvent is already transformed wrt local position, velocity, etc.
1925 QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, targetEvent->touchPoints().first(), event, item, false));
1926 if (target->childMouseEventFilter(item, mouseEvent.data())) {
1927 itemForTouchPointId[tp.id()] = target;
1928 touchMouseId = tp.id();
1929 target->grabMouse();
1936 return sendFilteredTouchEvent(target->parentItem(), item, event);
1939 bool QQuickWindowPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QEvent *event)
1944 QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target);
1945 if (targetPrivate->filtersChildMouseEvents)
1946 if (target->childMouseEventFilter(item, event))
1949 if (sendFilteredMouseEvent(target->parentItem(), item, event))
1955 bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event)
1957 QStyleHints *styleHints = qApp->styleHints();
1958 int caps = QGuiApplicationPrivate::mouseEventCaps(event);
1959 bool dragVelocityLimitAvailable = (caps & QTouchDevice::Velocity)
1960 && styleHints->startDragVelocity();
1961 bool overThreshold = qAbs(d) > styleHints->startDragDistance();
1962 if (dragVelocityLimitAvailable) {
1963 QVector2D velocityVec = QGuiApplicationPrivate::mouseEventVelocity(event);
1964 qreal velocity = axis == Qt::XAxis ? velocityVec.x() : velocityVec.y();
1965 overThreshold |= qAbs(velocity) > styleHints->startDragVelocity();
1967 return overThreshold;
1971 Propagates an event to a QQuickItem on the window
1973 bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e)
1978 qWarning("QQuickWindow::sendEvent: Cannot send event to a null item");
1984 switch (e->type()) {
1985 case QEvent::KeyPress:
1986 case QEvent::KeyRelease:
1988 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1989 while (!e->isAccepted() && (item = item->parentItem())) {
1991 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1994 case QEvent::FocusIn:
1995 case QEvent::FocusOut:
1996 QQuickItemPrivate::get(item)->deliverFocusEvent(static_cast<QFocusEvent *>(e));
1998 case QEvent::MouseButtonPress:
1999 case QEvent::MouseButtonRelease:
2000 case QEvent::MouseButtonDblClick:
2001 case QEvent::MouseMove:
2002 // XXX todo - should sendEvent be doing this? how does it relate to forwarded events?
2003 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
2004 // accept because qml items by default accept and have to explicitly opt out of accepting
2006 QQuickItemPrivate::get(item)->deliverMouseEvent(static_cast<QMouseEvent *>(e));
2009 case QEvent::UngrabMouse:
2010 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
2012 item->mouseUngrabEvent();
2016 QQuickItemPrivate::get(item)->deliverWheelEvent(static_cast<QWheelEvent *>(e));
2018 case QEvent::HoverEnter:
2019 case QEvent::HoverLeave:
2020 case QEvent::HoverMove:
2021 QQuickItemPrivate::get(item)->deliverHoverEvent(static_cast<QHoverEvent *>(e));
2023 case QEvent::TouchBegin:
2024 case QEvent::TouchUpdate:
2025 case QEvent::TouchEnd:
2026 d->sendFilteredTouchEvent(item->parentItem(), item, static_cast<QTouchEvent *>(e));
2028 case QEvent::TouchCancel:
2029 QQuickItemPrivate::get(item)->deliverTouchEvent(static_cast<QTouchEvent *>(e));
2031 #ifndef QT_NO_DRAGANDDROP
2032 case QEvent::DragEnter:
2033 case QEvent::DragMove:
2034 case QEvent::DragLeave:
2036 QQuickItemPrivate::get(item)->deliverDragEvent(e);
2046 void QQuickWindowPrivate::cleanupNodes()
2048 for (int ii = 0; ii < cleanupNodeList.count(); ++ii)
2049 delete cleanupNodeList.at(ii);
2050 cleanupNodeList.clear();
2053 void QQuickWindowPrivate::cleanupNodesOnShutdown(QQuickItem *item)
2055 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
2056 if (p->itemNodeInstance) {
2057 delete p->itemNodeInstance;
2058 p->itemNodeInstance = 0;
2060 if (p->extra.isAllocated()) {
2061 p->extra->opacityNode = 0;
2062 p->extra->clipNode = 0;
2063 p->extra->rootNode = 0;
2070 for (int ii = 0; ii < p->childItems.count(); ++ii)
2071 cleanupNodesOnShutdown(p->childItems.at(ii));
2074 // This must be called from the render thread, with the main thread frozen
2075 void QQuickWindowPrivate::cleanupNodesOnShutdown()
2079 cleanupNodesOnShutdown(rootItem);
2080 QSet<QQuickItem *>::const_iterator it = parentlessItems.begin();
2081 for (; it != parentlessItems.end(); ++it)
2082 cleanupNodesOnShutdown(*it);
2083 q->cleanupSceneGraph();
2086 void QQuickWindowPrivate::updateDirtyNodes()
2089 qWarning() << "QQuickWindowPrivate::updateDirtyNodes():";
2094 QQuickItem *updateList = dirtyItemList;
2096 if (updateList) QQuickItemPrivate::get(updateList)->prevDirtyItem = &updateList;
2098 while (updateList) {
2099 QQuickItem *item = updateList;
2100 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
2101 itemPriv->removeFromDirtyList();
2104 qWarning() << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString());
2106 updateDirtyNode(item);
2110 void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item)
2112 #ifdef QML_RUNTIME_TESTING
2113 bool didFlash = false;
2116 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
2117 quint32 dirty = itemPriv->dirtyAttributes;
2118 itemPriv->dirtyAttributes = 0;
2120 if ((dirty & QQuickItemPrivate::TransformUpdateMask) ||
2121 (dirty & QQuickItemPrivate::Size && itemPriv->origin() != QQuickItem::TopLeft &&
2122 (itemPriv->scale() != 1. || itemPriv->rotation() != 0.))) {
2126 if (itemPriv->x != 0. || itemPriv->y != 0.)
2127 matrix.translate(itemPriv->x, itemPriv->y);
2129 for (int ii = itemPriv->transforms.count() - 1; ii >= 0; --ii)
2130 itemPriv->transforms.at(ii)->applyTo(&matrix);
2132 if (itemPriv->scale() != 1. || itemPriv->rotation() != 0.) {
2133 QPointF origin = item->transformOriginPoint();
2134 matrix.translate(origin.x(), origin.y());
2135 if (itemPriv->scale() != 1.)
2136 matrix.scale(itemPriv->scale(), itemPriv->scale());
2137 if (itemPriv->rotation() != 0.)
2138 matrix.rotate(itemPriv->rotation(), 0, 0, 1);
2139 matrix.translate(-origin.x(), -origin.y());
2142 itemPriv->itemNode()->setMatrix(matrix);
2145 bool clipEffectivelyChanged = (dirty & (QQuickItemPrivate::Clip | QQuickItemPrivate::Window)) &&
2146 ((item->clip() == false) != (itemPriv->clipNode() == 0));
2147 int effectRefCount = itemPriv->extra.isAllocated()?itemPriv->extra->effectRefCount:0;
2148 bool effectRefEffectivelyChanged = (dirty & (QQuickItemPrivate::EffectReference | QQuickItemPrivate::Window)) &&
2149 ((effectRefCount == 0) != (itemPriv->rootNode() == 0));
2151 if (clipEffectivelyChanged) {
2152 QSGNode *parent = itemPriv->opacityNode() ? (QSGNode *) itemPriv->opacityNode() :
2153 (QSGNode *)itemPriv->itemNode();
2154 QSGNode *child = itemPriv->rootNode() ? (QSGNode *)itemPriv->rootNode() :
2155 (QSGNode *)itemPriv->groupNode;
2158 Q_ASSERT(itemPriv->clipNode() == 0);
2159 itemPriv->extra.value().clipNode = new QQuickDefaultClipNode(item->clipRect());
2160 itemPriv->clipNode()->update();
2163 parent->removeChildNode(child);
2164 parent->appendChildNode(itemPriv->clipNode());
2166 itemPriv->clipNode()->appendChildNode(child);
2169 Q_ASSERT(itemPriv->clipNode() != 0);
2170 parent->removeChildNode(itemPriv->clipNode());
2172 itemPriv->clipNode()->removeChildNode(child);
2173 delete itemPriv->clipNode();
2174 itemPriv->extra->clipNode = 0;
2176 parent->appendChildNode(child);
2180 if (dirty & QQuickItemPrivate::ChildrenUpdateMask)
2181 itemPriv->childContainerNode()->removeAllChildNodes();
2183 if (effectRefEffectivelyChanged) {
2184 QSGNode *parent = itemPriv->clipNode();
2186 parent = itemPriv->opacityNode();
2188 parent = itemPriv->itemNode();
2189 QSGNode *child = itemPriv->groupNode;
2191 if (itemPriv->extra.isAllocated() && itemPriv->extra->effectRefCount) {
2192 Q_ASSERT(itemPriv->rootNode() == 0);
2193 itemPriv->extra->rootNode = new QSGRootNode;
2196 parent->removeChildNode(child);
2197 parent->appendChildNode(itemPriv->rootNode());
2199 itemPriv->rootNode()->appendChildNode(child);
2201 Q_ASSERT(itemPriv->rootNode() != 0);
2202 parent->removeChildNode(itemPriv->rootNode());
2204 itemPriv->rootNode()->removeChildNode(child);
2205 delete itemPriv->rootNode();
2206 itemPriv->extra->rootNode = 0;
2208 parent->appendChildNode(child);
2212 if (dirty & QQuickItemPrivate::ChildrenUpdateMask) {
2213 QSGNode *groupNode = itemPriv->groupNode;
2215 groupNode->removeAllChildNodes();
2217 QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems();
2220 for (; ii < orderedChildren.count() && orderedChildren.at(ii)->z() < 0; ++ii) {
2221 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
2222 if (!childPrivate->explicitVisible &&
2223 (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount))
2225 if (childPrivate->itemNode()->parent())
2226 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
2228 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
2231 QSGNode *beforePaintNode = itemPriv->groupNode ? itemPriv->groupNode->lastChild() : 0;
2232 if (beforePaintNode || itemPriv->extra.isAllocated())
2233 itemPriv->extra.value().beforePaintNode = beforePaintNode;
2235 if (itemPriv->paintNode)
2236 itemPriv->childContainerNode()->appendChildNode(itemPriv->paintNode);
2238 for (; ii < orderedChildren.count(); ++ii) {
2239 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
2240 if (!childPrivate->explicitVisible &&
2241 (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount))
2243 if (childPrivate->itemNode()->parent())
2244 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
2246 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
2250 if ((dirty & QQuickItemPrivate::Size) && itemPriv->clipNode()) {
2251 itemPriv->clipNode()->setRect(item->clipRect());
2252 itemPriv->clipNode()->update();
2255 if (dirty & (QQuickItemPrivate::OpacityValue | QQuickItemPrivate::Visible
2256 | QQuickItemPrivate::HideReference | QQuickItemPrivate::Window))
2258 qreal opacity = itemPriv->explicitVisible && (!itemPriv->extra.isAllocated() || itemPriv->extra->hideRefCount == 0)
2259 ? itemPriv->opacity() : qreal(0);
2261 if (opacity != 1 && !itemPriv->opacityNode()) {
2262 itemPriv->extra.value().opacityNode = new QSGOpacityNode;
2264 QSGNode *parent = itemPriv->itemNode();
2265 QSGNode *child = itemPriv->clipNode();
2267 child = itemPriv->rootNode();
2269 child = itemPriv->groupNode;
2272 parent->removeChildNode(child);
2273 parent->appendChildNode(itemPriv->opacityNode());
2275 itemPriv->opacityNode()->appendChildNode(child);
2277 if (itemPriv->opacityNode())
2278 itemPriv->opacityNode()->setOpacity(opacity);
2281 if (dirty & QQuickItemPrivate::ContentUpdateMask) {
2283 if (itemPriv->flags & QQuickItem::ItemHasContents) {
2284 updatePaintNodeData.transformNode = itemPriv->itemNode();
2285 itemPriv->paintNode = item->updatePaintNode(itemPriv->paintNode, &updatePaintNodeData);
2287 Q_ASSERT(itemPriv->paintNode == 0 ||
2288 itemPriv->paintNode->parent() == 0 ||
2289 itemPriv->paintNode->parent() == itemPriv->childContainerNode());
2291 if (itemPriv->paintNode && itemPriv->paintNode->parent() == 0) {
2292 if (itemPriv->extra.isAllocated() && itemPriv->extra->beforePaintNode)
2293 itemPriv->childContainerNode()->insertChildNodeAfter(itemPriv->paintNode, itemPriv->extra->beforePaintNode);
2295 itemPriv->childContainerNode()->prependChildNode(itemPriv->paintNode);
2297 } else if (itemPriv->paintNode) {
2298 delete itemPriv->paintNode;
2299 itemPriv->paintNode = 0;
2304 // Check consistency.
2305 const QSGNode *nodeChain[] = {
2306 itemPriv->itemNodeInstance,
2307 itemPriv->opacityNode(),
2308 itemPriv->clipNode(),
2309 itemPriv->rootNode(),
2310 itemPriv->groupNode,
2311 itemPriv->paintNode,
2316 while (ip < 5 && nodeChain[ip] == 0)
2321 while (ic < 5 && nodeChain[ic] == 0)
2323 const QSGNode *parent = nodeChain[ip];
2324 const QSGNode *child = nodeChain[ic];
2326 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 0);
2328 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 1);
2329 Q_ASSERT(child->parent() == parent);
2330 bool containsChild = false;
2331 for (QSGNode *n = parent->firstChild(); n; n = n->nextSibling())
2332 containsChild |= (n == child);
2333 Q_ASSERT(containsChild);
2339 #ifdef QML_RUNTIME_TESTING
2340 if (itemPriv->sceneGraphContext()->isFlashModeEnabled()) {
2341 QSGFlashNode *flash = new QSGFlashNode();
2342 flash->setRect(item->boundingRect());
2343 itemPriv->childContainerNode()->appendChildNode(flash);
2354 void QQuickWindow::maybeUpdate()
2357 d->windowManager->maybeUpdate(this);
2360 void QQuickWindow::cleanupSceneGraph()
2367 delete d->renderer->rootNode();
2374 Returns the opengl context used for rendering.
2376 If the scene graph is not ready, this function will return 0.
2378 \sa sceneGraphInitialized(), sceneGraphInvalidated()
2381 QOpenGLContext *QQuickWindow::openglContext() const
2383 Q_D(const QQuickWindow);
2384 if (d->context->isReady())
2385 return d->context->glContext();
2391 \fn void QSGContext::sceneGraphInitialized()
2393 This signal is emitted when the scene graph has been initialized.
2395 This signal will be emitted from the scene graph rendering thread.
2401 \fn void QSGContext::sceneGraphInvalidated()
2403 This signal is emitted when the scene graph has been invalidated.
2405 This signal implies that the opengl rendering context used
2406 has been invalidated and all user resources tied to that context
2409 This signal will be emitted from the scene graph rendering thread.
2414 Sets the render target for this window to be \a fbo.
2416 The specified fbo must be created in the context of the window
2417 or one that shares with it.
2420 This function can only be called from the thread doing
2424 void QQuickWindow::setRenderTarget(QOpenGLFramebufferObject *fbo)
2427 if (d->context && d->context && QThread::currentThread() != d->context->thread()) {
2428 qWarning("QQuickWindow::setRenderThread: Cannot set render target from outside the rendering thread");
2432 d->renderTarget = fbo;
2434 d->renderTargetId = fbo->handle();
2435 d->renderTargetSize = fbo->size();
2437 d->renderTargetId = 0;
2438 d->renderTargetSize = QSize();
2445 void QQuickWindow::setRenderTarget(uint fboId, const QSize &size)
2448 if (d->context && d->context && QThread::currentThread() != d->context->thread()) {
2449 qWarning("QQuickWindow::setRenderThread: Cannot set render target from outside the rendering thread");
2453 d->renderTargetId = fboId;
2454 d->renderTargetSize = size;
2456 // Unset any previously set instance...
2457 d->renderTarget = 0;
2462 Returns the FBO id of the render target when set; otherwise returns 0.
2464 uint QQuickWindow::renderTargetId() const
2466 Q_D(const QQuickWindow);
2467 return d->renderTargetId;
2471 Returns the size of the currently set render target; otherwise returns an enpty size.
2473 QSize QQuickWindow::renderTargetSize() const
2475 Q_D(const QQuickWindow);
2476 return d->renderTargetSize;
2483 Returns the render target for this window.
2485 The default is to render to the surface of the window, in which
2486 case the render target is 0.
2488 QOpenGLFramebufferObject *QQuickWindow::renderTarget() const
2490 Q_D(const QQuickWindow);
2491 return d->renderTarget;
2496 Grabs the contents of the window and returns it as an image.
2498 This function might not work if the window is not visible.
2500 \warning Calling this function will cause performance problems.
2502 \warning This function can only be called from the GUI thread.
2504 QImage QQuickWindow::grabWindow()
2507 return d->windowManager->grab(this);
2511 Returns an incubation controller that splices incubation between frames
2512 for this window. QQuickView automatically installs this controller for you,
2513 otherwise you will need to install it yourself using \l{QQmlEngine::setIncubationController}
2515 The controller is owned by the window and will be destroyed when the window
2518 QQmlIncubationController *QQuickWindow::incubationController() const
2520 Q_D(const QQuickWindow);
2522 if (!d->incubationController)
2523 d->incubationController = new QQuickWindowIncubationController(const_cast<QQuickWindowPrivate *>(d));
2524 return d->incubationController;
2530 \enum QQuickWindow::CreateTextureOption
2532 The CreateTextureOption enums are used to customize a texture is wrapped.
2534 \value TextureHasAlphaChannel The texture has an alpha channel and should
2535 be drawn using blending.
2537 \value TextureHasMipmaps The texture has mipmaps and can be drawn with
2540 \value TextureOwnsGLTexture The texture object owns the texture id and
2541 will delete the GL texture when the texture object is deleted.
2545 \fn void QQuickWindow::beforeRendering()
2547 This signal is emitted before the scene starts rendering.
2549 Combined with the modes for clearing the background, this option
2550 can be used to paint using raw GL under QML content.
2552 The GL context used for rendering the scene graph will be bound
2555 \warning Since this signal is emitted from the scene graph rendering thread, the
2556 receiver should be on the scene graph thread or the connection should be Qt::DirectConnection.
2558 \warning Make very sure that a signal handler for beforeRendering leaves the GL
2559 context in the same state as it was when the signal handler was entered. Failing to
2560 do so can result in the scene not rendering properly.
2564 \fn void QQuickWindow::afterRendering()
2566 This signal is emitted after the scene has completed rendering, before swapbuffers is called.
2568 This signal can be used to paint using raw GL on top of QML content,
2569 or to do screen scraping of the current frame buffer.
2571 The GL context used for rendering the scene graph will be bound at this point.
2573 \warning Since this signal is emitted from the scene graph rendering thread, the
2574 receiver should be on the scene graph thread or the connection should be Qt::DirectConnection.
2576 \warning Make very sure that a signal handler for afterRendering() leaves the GL
2577 context in the same state as it was when the signal handler was entered. Failing to
2578 do so can result in the scene not rendering properly.
2584 Sets weither the scene graph rendering of QML should clear the color buffer
2585 before it starts rendering to \a enbled.
2587 By disabling clearing of the color buffer, it is possible to do GL painting
2588 under the scene graph.
2590 The color buffer is cleared by default.
2592 \sa beforeRendering()
2595 void QQuickWindow::setClearBeforeRendering(bool enabled)
2598 d->clearBeforeRendering = enabled;
2604 Returns weither clearing of the color buffer is done before rendering or not.
2607 bool QQuickWindow::clearBeforeRendering() const
2609 Q_D(const QQuickWindow);
2610 return d->clearBeforeRendering;
2616 Creates a new QSGTexture from the supplied \a image. If the image has an
2617 alpha channel, the corresponding texture will have an alpha channel.
2619 The caller of the function is responsible for deleting the returned texture.
2620 The actual GL texture will be deleted when the texture object is deleted.
2622 Depending on the underlying implementation of the scene graph, the returned
2623 texture may be part of an atlas. For code to be portable across implementations
2624 one should always use the texture coordinates returned from
2625 QSGTexture::normalizedTextureSubRect() when building geometry.
2627 \warning This function will return 0 if the scene graph has not yet been
2630 \warning The returned texture is not memory managed by the scene graph and
2631 must be explicitely deleted by the caller on the rendering thread.
2632 This is acheived by deleting the texture from a QSGNode destructor
2633 or by using deleteLater() in the case where the texture already has affinity
2634 to the rendering thread.
2636 This function can be called from any thread.
2638 \sa sceneGraphInitialized()
2641 QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const
2643 Q_D(const QQuickWindow);
2644 if (d->context && d->context->isReady())
2645 return d->context->createTexture(image);
2653 Creates a new QSGTexture object from an existing GL texture \a id.
2655 The caller of the function is responsible for deleting the returned texture.
2657 Use \a options to customize the texture attributes.
2659 \warning This function will return 0 if the scenegraph has not yet been
2662 \sa sceneGraphInitialized()
2664 QSGTexture *QQuickWindow::createTextureFromId(uint id, const QSize &size, CreateTextureOptions options) const
2666 Q_D(const QQuickWindow);
2667 if (d->context && d->context->isReady()) {
2668 QSGPlainTexture *texture = new QSGPlainTexture();
2669 texture->setTextureId(id);
2670 texture->setHasAlphaChannel(options & TextureHasAlphaChannel);
2671 texture->setHasMipmaps(options & TextureHasMipmaps);
2672 texture->setOwnsTexture(options & TextureOwnsGLTexture);
2673 texture->setTextureSize(size);
2681 Sets the color used to clear the opengl context to \a color.
2683 Setting the clear color has no effect when clearing is disabled.
2685 \sa setClearBeforeRendering()
2688 void QQuickWindow::setColor(const QColor &color)
2691 if (color == d->clearColor)
2694 d->clearColor = color;
2695 emit colorChanged(color);
2701 Returns the color used to clear the opengl context.
2704 QColor QQuickWindow::color() const
2706 return d_func()->clearColor;
2711 #include "moc_qquickwindow.cpp"