1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include "qquickwindow.h"
43 #include "qquickwindow_p.h"
45 #include "qquickitem.h"
46 #include "qquickitem_p.h"
47 #include "qquickevents_p_p.h"
49 #include <QtQuick/private/qsgrenderer_p.h>
50 #include <QtQuick/private/qsgtexture_p.h>
51 #include <QtQuick/private/qsgflashnode_p.h>
53 #include <private/qquickwindowmanager_p.h>
55 #include <private/qguiapplication_p.h>
56 #include <QtGui/QInputMethod>
58 #include <private/qabstractanimation_p.h>
60 #include <QtGui/qpainter.h>
61 #include <QtGui/qevent.h>
62 #include <QtGui/qmatrix4x4.h>
63 #include <QtGui/qstylehints.h>
64 #include <QtCore/qvarlengtharray.h>
65 #include <QtCore/qabstractanimation.h>
66 #include <QtQml/qqmlincubator.h>
68 #include <QtQuick/private/qquickpixmapcache_p.h>
70 #include <private/qqmlprofilerservice_p.h>
71 #include <private/qqmlmemoryprofiler_p.h>
75 void QQuickWindowPrivate::updateFocusItemTransform()
78 QQuickItem *focus = q->activeFocusItem();
79 if (focus && qApp->focusObject() == focus)
80 qApp->inputMethod()->setInputItemTransform(QQuickItemPrivate::get(focus)->itemToWindowTransform());
84 class QQuickWindowIncubationController : public QObject, public QQmlIncubationController
89 QQuickWindowIncubationController(const QQuickWindow *window)
90 : m_window(QQuickWindowPrivate::get(const_cast<QQuickWindow *>(window)))
92 // Allow incubation for 1/3 of a frame.
93 m_incubation_time = qMax(1, int(1000 / QGuiApplication::primaryScreen()->refreshRate()) / 3);
95 m_animation_driver = m_window->windowManager->animationDriver();
96 if (m_animation_driver) {
97 connect(m_animation_driver, SIGNAL(stopped()), this, SLOT(animationStopped()));
98 connect(window, SIGNAL(frameSwapped()), this, SLOT(incubate()));
103 virtual bool event(QEvent *e)
105 if (e->type() == QEvent::User) {
109 return QObject::event(e);
114 if (incubatingObjectCount()) {
115 if (m_animation_driver && m_animation_driver->isRunning()) {
116 incubateFor(m_incubation_time);
118 incubateFor(m_incubation_time * 2);
119 if (incubatingObjectCount())
120 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
125 void animationStopped() { incubate(); }
128 virtual void incubatingObjectCountChanged(int count)
130 if (count && (!m_animation_driver || !m_animation_driver->isRunning()))
131 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
135 QQuickWindowPrivate *m_window;
136 int m_incubation_time;
137 QAnimationDriver *m_animation_driver;
140 #include "qquickwindow.moc"
143 #ifndef QT_NO_ACCESSIBILITY
145 Returns an accessibility interface for this window, or 0 if such an
146 interface cannot be created.
148 \warning The caller is responsible for deleting the returned interface.
150 QAccessibleInterface *QQuickWindow::accessibleRoot() const
152 return QAccessible::queryAccessibleInterface(const_cast<QQuickWindow*>(this));
161 Prior to being added to a valid window items can set and clear focus with no
162 effect. Only once items are added to a window (by way of having a parent set that
163 already belongs to a window) do the focus rules apply. Focus goes back to
164 having no effect if an item is removed from a window.
166 When an item is moved into a new focus scope (either being added to a window
167 for the first time, or having its parent changed), if the focus scope already has
168 a scope focused item that takes precedence over the item being added. Otherwise,
169 the focus of the added tree is used. In the case of of a tree of items being
170 added to a window for the first time, which may have a conflicted focus state (two
171 or more items in one scope having focus set), the same rule is applied item by item -
172 thus the first item that has focus will get it (assuming the scope doesn't already
173 have a scope focused item), and the other items will have their focus cleared.
177 // #define FOCUS_DEBUG
178 // #define MOUSE_DEBUG
179 // #define TOUCH_DEBUG
180 // #define DIRTY_DEBUG
183 void printFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1);
186 QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData()
191 QQuickRootItem::QQuickRootItem()
196 void QQuickWindow::exposeEvent(QExposeEvent *)
199 d->windowManager->exposureChanged(this);
203 void QQuickWindow::resizeEvent(QResizeEvent *)
206 d->windowManager->resize(this, size());
210 void QQuickWindow::showEvent(QShowEvent *)
212 d_func()->windowManager->show(this);
216 void QQuickWindow::hideEvent(QHideEvent *)
218 d_func()->windowManager->hide(this);
222 void QQuickWindow::focusOutEvent(QFocusEvent *)
225 d->contentItem->setFocus(false);
229 void QQuickWindow::focusInEvent(QFocusEvent *)
232 d->contentItem->setFocus(true);
233 d->updateFocusItemTransform();
237 void QQuickWindowPrivate::polishItems()
239 int maxPolishCycles = 100000;
241 while (!itemsToPolish.isEmpty() && --maxPolishCycles > 0) {
242 QSet<QQuickItem *> itms = itemsToPolish;
243 itemsToPolish.clear();
245 for (QSet<QQuickItem *>::iterator it = itms.begin(); it != itms.end(); ++it) {
246 QQuickItem *item = *it;
247 QQuickItemPrivate::get(item)->polishScheduled = false;
248 item->updatePolish();
252 if (maxPolishCycles == 0)
253 qWarning("QQuickWindow: possible QQuickItem::polish() loop");
255 updateFocusItemTransform();
259 * This parameter enables that this window can be rendered without
260 * being shown on screen. This feature is very limited in what it supports.
262 * For this feature to be useful one needs to hook into beforeRender()
263 * and set the render target.
266 void QQuickWindowPrivate::setRenderWithoutShowing(bool render)
268 if (render == renderWithoutShowing)
272 renderWithoutShowing = render;
275 windowManager->show(q);
277 windowManager->hide(q);
282 * Schedules the window to render another frame.
284 * Calling QQuickWindow::update() differs from QQuickItem::update() in that
285 * it always triggers a repaint, regardless of changes in the underlying
286 * scene graph or not.
288 void QQuickWindow::update()
291 d->windowManager->update(this);
294 void forceUpdate(QQuickItem *item)
296 if (item->flags() & QQuickItem::ItemHasContents)
298 QQuickItemPrivate::get(item)->dirty(QQuickItemPrivate::ChildrenUpdateMask);
300 QList <QQuickItem *> items = item->childItems();
301 for (int i=0; i<items.size(); ++i)
302 forceUpdate(items.at(i));
305 void QQuickWindowPrivate::syncSceneGraph()
307 QML_MEMORY_SCOPE_STRING("SceneGraph");
310 emit q->beforeSynchronizing();
312 forceUpdate(contentItem);
314 QSGRootNode *rootNode = new QSGRootNode;
315 rootNode->appendChildNode(QQuickItemPrivate::get(contentItem)->itemNode());
316 renderer = context->createRenderer();
317 renderer->setRootNode(rootNode);
322 // Copy the current state of clearing from window into renderer.
323 renderer->setClearColor(clearColor);
324 QSGRenderer::ClearMode mode = QSGRenderer::ClearStencilBuffer | QSGRenderer::ClearDepthBuffer;
325 if (clearBeforeRendering)
326 mode |= QSGRenderer::ClearColorBuffer;
327 renderer->setClearMode(mode);
331 void QQuickWindowPrivate::renderSceneGraph(const QSize &size)
333 QML_MEMORY_SCOPE_STRING("SceneGraph");
335 emit q->beforeRendering();
337 renderer->setDeviceRect(QRect(QPoint(0, 0), size));
338 if (renderTargetId) {
339 fboId = renderTargetId;
340 renderer->setViewportRect(QRect(QPoint(0, 0), renderTargetSize));
342 renderer->setViewportRect(QRect(QPoint(0, 0), size));
344 renderer->setProjectionMatrixToDeviceRect();
346 context->renderNextFrame(renderer, fboId);
347 emit q->afterRendering();
350 QQuickWindowPrivate::QQuickWindowPrivate()
353 , mouseGrabberItem(0)
358 , touchMousePressTimestamp(0)
359 , renderWithoutShowing(false)
364 , clearColor(Qt::white)
365 , clearBeforeRendering(true)
366 , persistentGLContext(false)
367 , persistentSceneGraph(false)
368 , lastWheelEventAccepted(false)
371 , incubationController(0)
375 QQuickWindowPrivate::~QQuickWindowPrivate()
379 void QQuickWindowPrivate::init(QQuickWindow *c)
385 contentItem = new QQuickRootItem;
386 QQmlEngine::setObjectOwnership(contentItem, QQmlEngine::CppOwnership);
387 QQuickItemPrivate *contentItemPrivate = QQuickItemPrivate::get(contentItem);
388 contentItemPrivate->window = q;
389 contentItemPrivate->windowRefCount = 1;
390 contentItemPrivate->flags |= QQuickItem::ItemIsFocusScope;
392 // In the absence of a focus in event on some platforms assume the window will
393 // be activated immediately and set focus on the contentItem
394 // ### Remove when QTBUG-22415 is resolved.
395 //It is important that this call happens after the contentItem has a window..
396 contentItem->setFocus(true);
398 windowManager = QQuickWindowManager::instance();
399 context = windowManager->sceneGraphContext();
400 q->setSurfaceType(QWindow::OpenGLSurface);
401 q->setFormat(context->defaultSurfaceFormat());
403 QObject::connect(context, SIGNAL(initialized()), q, SIGNAL(sceneGraphInitialized()), Qt::DirectConnection);
404 QObject::connect(context, SIGNAL(invalidated()), q, SIGNAL(sceneGraphInvalidated()), Qt::DirectConnection);
405 QObject::connect(context, SIGNAL(invalidated()), q, SLOT(cleanupSceneGraph()), Qt::DirectConnection);
409 \property QQuickWindow::data
413 QQmlListProperty<QObject> QQuickWindowPrivate::data()
416 return QQuickItemPrivate::get(contentItem)->data();
419 void QQuickWindowPrivate::initContentItem()
422 q->connect(q, SIGNAL(widthChanged(int)),
423 contentItem, SLOT(setWidth(int)));
424 q->connect(q, SIGNAL(heightChanged(int)),
425 contentItem, SLOT(setHeight(int)));
426 contentItem->setWidth(q->width());
427 contentItem->setHeight(q->height());
430 static QMouseEvent *touchToMouseEvent(QEvent::Type type, const QTouchEvent::TouchPoint &p, QTouchEvent *event, QQuickItem *item, bool transformNeeded = true)
432 // The touch point local position and velocity are not yet transformed.
433 QMouseEvent *me = new QMouseEvent(type, transformNeeded ? item->mapFromScene(p.scenePos()) : p.pos(), p.scenePos(), p.screenPos(),
434 Qt::LeftButton, Qt::LeftButton, event->modifiers());
435 me->setAccepted(true);
436 me->setTimestamp(event->timestamp());
437 QVector2D transformedVelocity = p.velocity();
438 if (transformNeeded) {
439 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
440 QMatrix4x4 transformMatrix(itemPrivate->windowToItemTransform());
441 transformedVelocity = transformMatrix.mapVector(p.velocity()).toVector2D();
443 QGuiApplicationPrivate::setMouseEventCapsAndVelocity(me, event->device()->capabilities(), transformedVelocity);
447 bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *event)
450 // For each point, check if it is accepted, if not, try the next point.
451 // Any of the fingers can become the mouse one.
452 // This can happen because a mouse area might not accept an event at some point but another.
453 for (int i = 0; i < event->touchPoints().count(); ++i) {
454 const QTouchEvent::TouchPoint &p = event->touchPoints().at(i);
456 if (touchMouseId == -1 && p.state() & Qt::TouchPointPressed) {
457 QPointF pos = item->mapFromScene(p.scenePos());
459 // probably redundant, we check bounds in the calling function (matchingNewPoints)
460 if (!item->contains(pos))
463 bool doubleClick = event->timestamp() - touchMousePressTimestamp
464 < static_cast<ulong>(qApp->styleHints()->mouseDoubleClickInterval());
465 touchMousePressTimestamp = event->timestamp();
466 // Store the id already here and restore it to -1 if the event does not get
467 // accepted. Cannot defer setting the new value because otherwise if the event
468 // handler spins the event loop all subsequent moves and releases get lost.
469 touchMouseId = p.id();
470 itemForTouchPointId[touchMouseId] = item;
471 QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event, item));
473 // Send a single press and see if that's accepted
474 if (!mouseGrabberItem)
476 item->grabTouchPoints(QVector<int>() << touchMouseId);
478 q->sendEvent(item, mousePress.data());
479 event->setAccepted(mousePress->isAccepted());
480 if (!mousePress->isAccepted()) {
482 if (itemForTouchPointId.value(p.id()) == item)
483 itemForTouchPointId.remove(p.id());
485 if (mouseGrabberItem == item)
489 if (doubleClick && mousePress->isAccepted()) {
490 touchMousePressTimestamp = 0;
491 QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item));
492 q->sendEvent(item, mouseDoubleClick.data());
493 event->setAccepted(mouseDoubleClick->isAccepted());
494 if (mouseDoubleClick->isAccepted()) {
500 // The event was accepted, we are done.
501 if (mousePress->isAccepted())
503 // The event was not accepted but touchMouseId was set.
504 if (touchMouseId != -1)
506 // try the next point
508 // Touch point was there before and moved
509 } else if (p.id() == touchMouseId) {
510 if (p.state() & Qt::TouchPointMoved) {
511 if (mouseGrabberItem) {
512 QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, mouseGrabberItem));
513 q->sendEvent(mouseGrabberItem, me.data());
514 event->setAccepted(me->isAccepted());
515 if (me->isAccepted()) {
516 itemForTouchPointId[p.id()] = mouseGrabberItem; // N.B. the mouseGrabberItem may be different after returning from sendEvent()
520 // no grabber, check if we care about mouse hover
521 // FIXME: this should only happen once, not recursively... I'll ignore it just ignore hover now.
522 // hover for touch???
523 QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, item));
524 if (lastMousePosition.isNull())
525 lastMousePosition = me->windowPos();
526 QPointF last = lastMousePosition;
527 lastMousePosition = me->windowPos();
529 bool accepted = me->isAccepted();
530 bool delivered = deliverHoverEvent(contentItem, me->windowPos(), last, me->modifiers(), accepted);
532 //take care of any exits
533 accepted = clearHover();
535 me->setAccepted(accepted);
538 } else if (p.state() & Qt::TouchPointReleased) {
539 // currently handled point was released
541 if (mouseGrabberItem) {
542 QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseButtonRelease, p, event, mouseGrabberItem));
543 q->sendEvent(mouseGrabberItem, me.data());
544 if (mouseGrabberItem) // might have ungrabbed due to event
545 mouseGrabberItem->ungrabMouse();
546 return me->isAccepted();
555 void QQuickWindowPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform)
557 QMatrix4x4 transformMatrix(transform);
558 for (int i=0; i<touchPoints.count(); i++) {
559 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
560 touchPoint.setRect(transform.mapRect(touchPoint.sceneRect()));
561 touchPoint.setStartPos(transform.map(touchPoint.startScenePos()));
562 touchPoint.setLastPos(transform.map(touchPoint.lastScenePos()));
563 touchPoint.setVelocity(transformMatrix.mapVector(touchPoint.velocity()).toVector2D());
569 Translates the data in \a touchEvent to this window. This method leaves the item local positions in
570 \a touchEvent untouched (these are filled in later).
572 void QQuickWindowPrivate::translateTouchEvent(QTouchEvent *touchEvent)
574 QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
575 for (int i = 0; i < touchPoints.count(); ++i) {
576 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
578 touchPoint.setScreenRect(touchPoint.sceneRect());
579 touchPoint.setStartScreenPos(touchPoint.startScenePos());
580 touchPoint.setLastScreenPos(touchPoint.lastScenePos());
582 touchPoint.setSceneRect(touchPoint.rect());
583 touchPoint.setStartScenePos(touchPoint.startPos());
584 touchPoint.setLastScenePos(touchPoint.lastPos());
587 lastMousePosition = touchPoint.pos().toPoint();
589 touchEvent->setTouchPoints(touchPoints);
592 void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
597 Q_ASSERT(scope || item == contentItem);
600 qWarning() << "QQuickWindowPrivate::setFocusInScope():";
601 qWarning() << " scope:" << (QObject *)scope;
603 qWarning() << " scopeSubFocusItem:" << (QObject *)QQuickItemPrivate::get(scope)->subFocusItem;
604 qWarning() << " item:" << (QObject *)item;
605 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
608 QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0;
609 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
611 QQuickItem *oldActiveFocusItem = 0;
612 QQuickItem *newActiveFocusItem = 0;
614 QVarLengthArray<QQuickItem *, 20> changed;
616 // Does this change the active focus?
617 if (item == contentItem || (scopePrivate->activeFocus && item->isEnabled())) {
618 oldActiveFocusItem = activeFocusItem;
619 newActiveFocusItem = item;
620 while (newActiveFocusItem->isFocusScope()
621 && newActiveFocusItem->scopedFocusItem()
622 && newActiveFocusItem->scopedFocusItem()->isEnabled()) {
623 newActiveFocusItem = newActiveFocusItem->scopedFocusItem();
626 if (oldActiveFocusItem) {
628 qApp->inputMethod()->commit();
632 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
633 q->sendEvent(oldActiveFocusItem, &event);
635 QQuickItem *afi = oldActiveFocusItem;
636 while (afi && afi != scope) {
637 if (QQuickItemPrivate::get(afi)->activeFocus) {
638 QQuickItemPrivate::get(afi)->activeFocus = false;
641 afi = afi->parentItem();
646 if (item != contentItem && !(options & DontChangeSubFocusItem)) {
647 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
648 if (oldSubFocusItem) {
649 QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
650 changed << oldSubFocusItem;
653 QQuickItemPrivate::get(item)->updateSubFocusItem(scope, true);
656 if (!(options & DontChangeFocusProperty)) {
657 // if (item != contentItem || QGuiApplication::focusWindow() == q) { // QTBUG-22415
658 itemPrivate->focus = true;
663 if (newActiveFocusItem && contentItem->hasFocus()) {
664 activeFocusItem = newActiveFocusItem;
666 QQuickItemPrivate::get(newActiveFocusItem)->activeFocus = true;
667 changed << newActiveFocusItem;
669 QQuickItem *afi = newActiveFocusItem->parentItem();
670 while (afi && afi != scope) {
671 if (afi->isFocusScope()) {
672 QQuickItemPrivate::get(afi)->activeFocus = true;
675 afi = afi->parentItem();
678 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
679 q->sendEvent(newActiveFocusItem, &event);
682 emit q->focusObjectChanged(activeFocusItem);
684 if (!changed.isEmpty())
685 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
688 void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
693 Q_ASSERT(scope || item == contentItem);
696 qWarning() << "QQuickWindowPrivate::clearFocusInScope():";
697 qWarning() << " scope:" << (QObject *)scope;
698 qWarning() << " item:" << (QObject *)item;
699 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
702 QQuickItemPrivate *scopePrivate = 0;
704 scopePrivate = QQuickItemPrivate::get(scope);
705 if ( !scopePrivate->subFocusItem )
706 return;//No focus, nothing to do.
709 QQuickItem *oldActiveFocusItem = 0;
710 QQuickItem *newActiveFocusItem = 0;
712 QVarLengthArray<QQuickItem *, 20> changed;
714 Q_ASSERT(item == contentItem || item == scopePrivate->subFocusItem);
716 // Does this change the active focus?
717 if (item == contentItem || scopePrivate->activeFocus) {
718 oldActiveFocusItem = activeFocusItem;
719 newActiveFocusItem = scope;
721 Q_ASSERT(oldActiveFocusItem);
724 qApp->inputMethod()->commit();
728 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
729 q->sendEvent(oldActiveFocusItem, &event);
731 QQuickItem *afi = oldActiveFocusItem;
732 while (afi && afi != scope) {
733 if (QQuickItemPrivate::get(afi)->activeFocus) {
734 QQuickItemPrivate::get(afi)->activeFocus = false;
737 afi = afi->parentItem();
741 if (item != contentItem && !(options & DontChangeSubFocusItem)) {
742 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
743 if (oldSubFocusItem && !(options & DontChangeFocusProperty)) {
744 QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
745 changed << oldSubFocusItem;
748 QQuickItemPrivate::get(item)->updateSubFocusItem(scope, false);
750 } else if (!(options & DontChangeFocusProperty)) {
751 QQuickItemPrivate::get(item)->focus = false;
755 if (newActiveFocusItem) {
756 Q_ASSERT(newActiveFocusItem == scope);
757 activeFocusItem = scope;
759 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
760 q->sendEvent(newActiveFocusItem, &event);
763 emit q->focusObjectChanged(activeFocusItem);
765 if (!changed.isEmpty())
766 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
769 void QQuickWindowPrivate::notifyFocusChangesRecur(QQuickItem **items, int remaining)
771 QQmlGuard<QQuickItem> item(*items);
774 notifyFocusChangesRecur(items + 1, remaining - 1);
777 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
779 if (itemPrivate->notifiedFocus != itemPrivate->focus) {
780 itemPrivate->notifiedFocus = itemPrivate->focus;
781 emit item->focusChanged(itemPrivate->focus);
784 if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) {
785 itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus;
786 itemPrivate->itemChange(QQuickItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus);
787 emit item->activeFocusChanged(itemPrivate->activeFocus);
792 void QQuickWindowPrivate::dirtyItem(QQuickItem *)
798 void QQuickWindowPrivate::cleanup(QSGNode *n)
802 Q_ASSERT(!cleanupNodeList.contains(n));
803 cleanupNodeList.append(n);
809 \instantiates QQuickWindow
810 \inqmlmodule QtQuick.Window 2
811 \ingroup qtquick-visual
812 \brief Creates a new top-level window
814 The Window object creates a new top-level window for a QtQuick scene. It automatically sets up the
815 window for use with QtQuick 2.0 graphical types.
817 To use this type, you will need to import the module with the following line:
819 import QtQuick.Window 2.0
822 Restricting this import will allow you to have a QML environment without access to window system features.
830 \brief The QQuickWindow class provides the window for displaying a graphical QML scene
832 QQuickWindow provides the graphical scene management needed to interact with and display
833 a scene of QQuickItems.
835 A QQuickWindow always has a single invisible root item. To add items to this window,
836 reparent the items to the root item or to an existing item in the scene.
838 For easily displaying a scene from a QML file, see \l{QQuickView}.
840 \section1 Scene Graph and Rendering
842 The QQuickWindow uses a scene graph on top of OpenGL to render. This scene graph is disconnected
843 from the QML scene and potentially lives in another thread, depending on the platform
844 implementation. Since the rendering scene graph lives independently from the QML scene, it can
845 also be completely released without affecting the state of the QML scene.
847 The sceneGraphInitialized() signal is emitted on the rendering thread before the QML scene is
848 rendered to the screen for the first time. If the rendering scene graph has been released
849 the signal will be emitted again before the next frame is rendered.
851 The rendering of each frame is broken down into the following
852 steps, in the given order:
856 \li The QQuickWindow::beforeSynchronizing() signal is emitted.
857 Applications can make direct connections (Qt::DirectConnection)
858 to this signal to do any preparation required before calls to
859 QQuickItem::updatePaintNode().
861 \li Synchronization of the QML state into the scene graph. This is
862 done by calling the QQuickItem::updatePaintNode() function on all
863 items that have changed since the previous frame. When a dedicated
864 rendering thread is used, the GUI thread is blocked during this
865 synchroniation. This is the only time the QML items and the nodes
866 in the scene graph interact.
868 \li The window to be rendered is made current using
869 QOpenGLContext::makeCurrent().
871 \li The QQuickWindow::beforeRendering() signal is
872 emitted. Applications can make direct connections
873 (Qt::DirectConnection) to this signal to use custom OpenGL calls
874 which will then stack visually beneath the QML scene.
876 \li Items that have specified QSGNode::UsesPreprocess, will have their
877 QSGNode::preprocess() function invoked.
879 \li The QQuickWindow is cleared according to what is specified
880 using QQuickWindow::setClearBeforeRendering() and
881 QQuickWindow::setClearColor().
883 \li The scene graph is rendered.
885 \li The QQuickWindow::afterRendering() signal is
886 emitted. Applications can make direct connections
887 (Qt::DirectConnection) to this signal to use custom OpenGL calls
888 which will then stack visually over the QML scene.
890 \li The rendered frame is swapped and QQuickWindow::frameSwapped()
895 All of the above happen on the rendering thread, when applicable.
897 While the scene graph is being rendered on the rendering thread, the GUI will process animations
898 for the next frame. This means that as long as users are not using scene graph API
899 directly, the added complexity of a rendering thread can be completely ignored.
901 When a QQuickWindow is programatically hidden with hide() or setVisible(false), it will
902 stop rendering and its scene graph and OpenGL context might be released. The
903 sceneGraphInvalidated() signal will be emitted when this happens.
905 \warning It is crucial that OpenGL operations and interaction with the scene graph happens
906 exclusively on the rendering thread, primarily during the updatePaintNode() phase.
908 \warning As signals related to rendering might be emitted from the rendering thread,
909 connections should be made using Qt::DirectConnection
912 \section2 Resource Management
914 QML will typically try to cache images, scene graph nodes, etc to improve performance, but in
915 some low-memory scenarios it might be required to aggressively release these resources. The
916 releaseResources() can be used to force clean up of certain resources. Calling releaseResources()
917 may result in the entire scene graph and its OpenGL context being deleted. The
918 sceneGraphInvalidated() signal will be emitted when this happens.
920 \sa {OpenGL Under QML Example}
925 Constructs a window for displaying a QML scene with parent window \a parent.
927 QQuickWindow::QQuickWindow(QWindow *parent)
928 : QWindow(*(new QQuickWindowPrivate), parent)
937 QQuickWindow::QQuickWindow(QQuickWindowPrivate &dd, QWindow *parent)
938 : QWindow(dd, parent)
947 QQuickWindow::~QQuickWindow()
951 d->windowManager->windowDestroyed(this);
953 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
954 delete d->incubationController; d->incubationController = 0;
956 delete d->contentItem; d->contentItem = 0;
962 This function tries to release redundant resources currently held by the QML scene.
964 Calling this function might result in the scene graph and the OpenGL context used
965 for rendering being released to release graphics memory. If this happens, the
966 sceneGraphInvalidated() signal will be called, allowing users to clean up their
967 own graphics resources. The setPersistentOpenGLContext() and setPersistentSceneGraph()
968 functions can be used to prevent this from happening, if handling the cleanup is
969 not feasible in the application, at the cost of higher memory usage.
971 \sa sceneGraphInvalidated(), setPersistentOpenGLContext(), setPersistentSceneGraph()
974 void QQuickWindow::releaseResources()
977 d->windowManager->releaseResources();
978 QQuickPixmap::purgeCache();
984 Sets whether the OpenGL context can be released as a part of a call to
985 releaseResources() to \a persistent.
987 The OpenGL context might still be released when the user makes an explicit
990 \sa setPersistentSceneGraph()
993 void QQuickWindow::setPersistentOpenGLContext(bool persistent)
996 d->persistentGLContext = persistent;
1001 Returns whether the OpenGL context can be released as a part of a call to
1005 bool QQuickWindow::isPersistentOpenGLContext() const
1007 Q_D(const QQuickWindow);
1008 return d->persistentGLContext;
1014 Sets whether the scene graph nodes and resources can be released as a
1015 part of a call to releaseResources() to \a persistent.
1017 The scene graph nodes and resources might still be released when the user
1018 makes an explicit call to hide().
1020 \sa setPersistentOpenGLContext()
1023 void QQuickWindow::setPersistentSceneGraph(bool persistent)
1026 d->persistentSceneGraph = persistent;
1032 Returns whether the scene graph nodes and resources can be released as a part
1033 of a call to releaseResources().
1036 bool QQuickWindow::isPersistentSceneGraph() const
1038 Q_D(const QQuickWindow);
1039 return d->persistentSceneGraph;
1047 \property QQuickWindow::contentItem
1048 \brief The invisible root item of the scene.
1050 A QQuickWindow always has a single invisible root item containing all of its content.
1051 To add items to this window, reparent the items to the contentItem or to an existing
1054 QQuickItem *QQuickWindow::contentItem() const
1056 Q_D(const QQuickWindow);
1058 return d->contentItem;
1062 Returns the item which currently has active focus.
1064 QQuickItem *QQuickWindow::activeFocusItem() const
1066 Q_D(const QQuickWindow);
1068 return d->activeFocusItem;
1075 QObject *QQuickWindow::focusObject() const
1077 Q_D(const QQuickWindow);
1079 if (d->activeFocusItem)
1080 return d->activeFocusItem;
1081 return const_cast<QQuickWindow*>(this);
1086 Returns the item which currently has the mouse grab.
1088 QQuickItem *QQuickWindow::mouseGrabberItem() const
1090 Q_D(const QQuickWindow);
1092 return d->mouseGrabberItem;
1096 bool QQuickWindowPrivate::clearHover()
1099 if (hoverItems.isEmpty())
1102 QPointF pos = q->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint());
1104 bool accepted = false;
1105 foreach (QQuickItem* item, hoverItems)
1106 accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), true) || accepted;
1112 bool QQuickWindow::event(QEvent *e)
1116 switch (e->type()) {
1118 case QEvent::TouchBegin:
1119 case QEvent::TouchUpdate:
1120 case QEvent::TouchEnd: {
1121 QTouchEvent *touch = static_cast<QTouchEvent*>(e);
1122 d->translateTouchEvent(touch);
1123 // return in order to avoid the QWindow::event below
1124 return d->deliverTouchEvent(touch);
1127 case QEvent::TouchCancel:
1128 // return in order to avoid the QWindow::event below
1129 return d->deliverTouchCancelEvent(static_cast<QTouchEvent*>(e));
1133 d->lastMousePosition = QPoint();
1135 #ifndef QT_NO_DRAGANDDROP
1136 case QEvent::DragEnter:
1137 case QEvent::DragLeave:
1138 case QEvent::DragMove:
1140 d->deliverDragEvent(&d->dragGrabber, e);
1143 case QEvent::WindowDeactivate:
1144 contentItem()->windowDeactivateEvent();
1146 case QEvent::FocusAboutToChange:
1147 if (d->activeFocusItem)
1148 qGuiApp->inputMethod()->commit();
1154 return QWindow::event(e);
1158 void QQuickWindow::keyPressEvent(QKeyEvent *e)
1162 if (d->activeFocusItem)
1163 sendEvent(d->activeFocusItem, e);
1167 void QQuickWindow::keyReleaseEvent(QKeyEvent *e)
1171 if (d->activeFocusItem)
1172 sendEvent(d->activeFocusItem, e);
1175 QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos)
1177 int caps = QGuiApplicationPrivate::mouseEventCaps(event);
1178 QVector2D velocity = QGuiApplicationPrivate::mouseEventVelocity(event);
1179 QMouseEvent *me = new QMouseEvent(event->type(),
1180 transformedLocalPos ? *transformedLocalPos : event->localPos(),
1181 event->windowPos(), event->screenPos(),
1182 event->button(), event->buttons(), event->modifiers());
1183 QGuiApplicationPrivate::setMouseEventCapsAndVelocity(me, caps, velocity);
1184 me->setTimestamp(event->timestamp());
1188 bool QQuickWindowPrivate::deliverInitialMousePressEvent(QQuickItem *item, QMouseEvent *event)
1192 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1194 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1195 QPointF p = item->mapFromScene(event->windowPos());
1196 if (!item->contains(p))
1200 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1201 for (int ii = children.count() - 1; ii >= 0; --ii) {
1202 QQuickItem *child = children.at(ii);
1203 if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled)
1205 if (deliverInitialMousePressEvent(child, event))
1209 if (itemPrivate->acceptedMouseButtons() & event->button()) {
1210 QPointF localPos = item->mapFromScene(event->windowPos());
1211 if (item->contains(localPos)) {
1212 QScopedPointer<QMouseEvent> me(cloneMouseEvent(event, &localPos));
1215 q->sendEvent(item, me.data());
1216 event->setAccepted(me->isAccepted());
1217 if (me->isAccepted())
1219 if (mouseGrabberItem && !event->buttons())
1220 mouseGrabberItem->ungrabMouse();
1227 bool QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event)
1231 lastMousePosition = event->windowPos();
1233 if (!mouseGrabberItem &&
1234 event->type() == QEvent::MouseButtonPress &&
1235 (event->buttons() & event->button()) == event->buttons()) {
1236 if (deliverInitialMousePressEvent(contentItem, event))
1240 return event->isAccepted();
1243 if (mouseGrabberItem) {
1244 QPointF localPos = mouseGrabberItem->mapFromScene(event->windowPos());
1245 QScopedPointer<QMouseEvent> me(cloneMouseEvent(event, &localPos));
1247 q->sendEvent(mouseGrabberItem, me.data());
1248 event->setAccepted(me->isAccepted());
1249 if (me->isAccepted())
1257 void QQuickWindow::mousePressEvent(QMouseEvent *event)
1261 qWarning() << "QQuickWindow::mousePressEvent()" << event->localPos() << event->button() << event->buttons();
1264 d->deliverMouseEvent(event);
1268 void QQuickWindow::mouseReleaseEvent(QMouseEvent *event)
1272 qWarning() << "QQuickWindow::mouseReleaseEvent()" << event->localPos() << event->button() << event->buttons();
1275 if (!d->mouseGrabberItem) {
1276 QWindow::mouseReleaseEvent(event);
1280 d->deliverMouseEvent(event);
1281 if (d->mouseGrabberItem && !event->buttons())
1282 d->mouseGrabberItem->ungrabMouse();
1286 void QQuickWindow::mouseDoubleClickEvent(QMouseEvent *event)
1290 qWarning() << "QQuickWindow::mouseDoubleClickEvent()" << event->localPos() << event->button() << event->buttons();
1293 if (!d->mouseGrabberItem && (event->buttons() & event->button()) == event->buttons()) {
1294 if (d->deliverInitialMousePressEvent(d->contentItem, event))
1301 d->deliverMouseEvent(event);
1304 bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
1305 const QPointF &scenePos, const QPointF &lastScenePos,
1306 Qt::KeyboardModifiers modifiers, bool accepted)
1309 const QTransform transform = QQuickItemPrivate::get(item)->windowToItemTransform();
1311 //create copy of event
1312 QHoverEvent hoverEvent(type, transform.map(scenePos), transform.map(lastScenePos), modifiers);
1313 hoverEvent.setAccepted(accepted);
1315 q->sendEvent(item, &hoverEvent);
1317 return hoverEvent.isAccepted();
1321 void QQuickWindow::mouseMoveEvent(QMouseEvent *event)
1325 qWarning() << "QQuickWindow::mouseMoveEvent()" << event->localPos() << event->button() << event->buttons();
1328 #ifndef QT_NO_CURSOR
1329 d->updateCursor(event->windowPos());
1332 if (!d->mouseGrabberItem) {
1333 if (d->lastMousePosition.isNull())
1334 d->lastMousePosition = event->windowPos();
1335 QPointF last = d->lastMousePosition;
1336 d->lastMousePosition = event->windowPos();
1338 bool accepted = event->isAccepted();
1339 bool delivered = d->deliverHoverEvent(d->contentItem, event->windowPos(), last, event->modifiers(), accepted);
1341 //take care of any exits
1342 accepted = d->clearHover();
1344 event->setAccepted(accepted);
1348 d->deliverMouseEvent(event);
1351 bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
1352 Qt::KeyboardModifiers modifiers, bool &accepted)
1354 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1356 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1357 QPointF p = item->mapFromScene(scenePos);
1358 if (!item->contains(p))
1362 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1363 for (int ii = children.count() - 1; ii >= 0; --ii) {
1364 QQuickItem *child = children.at(ii);
1365 if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled)
1367 if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, accepted))
1371 if (itemPrivate->hoverEnabled) {
1372 QPointF p = item->mapFromScene(scenePos);
1373 if (item->contains(p)) {
1374 if (!hoverItems.isEmpty() && hoverItems[0] == item) {
1376 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1378 QList<QQuickItem *> itemsToHover;
1379 QQuickItem* parent = item;
1380 itemsToHover << item;
1381 while ((parent = parent->parentItem()))
1382 itemsToHover << parent;
1384 // Leaving from previous hovered items until we reach the item or one of its ancestors.
1385 while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems[0])) {
1386 sendHoverEvent(QEvent::HoverLeave, hoverItems[0], scenePos, lastScenePos, modifiers, accepted);
1387 hoverItems.removeFirst();
1390 if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item
1391 // ### Shouldn't we send moves for the parent items as well?
1392 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1394 // Enter items that are not entered yet.
1396 if (!hoverItems.isEmpty())
1397 startIdx = itemsToHover.indexOf(hoverItems[0]) - 1;
1399 startIdx = itemsToHover.count() - 1;
1401 for (int i = startIdx; i >= 0; i--) {
1402 QQuickItem *itemToHover = itemsToHover[i];
1403 if (QQuickItemPrivate::get(itemToHover)->hoverEnabled) {
1404 hoverItems.prepend(itemToHover);
1405 sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, accepted);
1417 bool QQuickWindowPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event)
1420 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1422 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1423 QPointF p = item->mapFromScene(event->posF());
1424 if (!item->contains(p))
1428 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1429 for (int ii = children.count() - 1; ii >= 0; --ii) {
1430 QQuickItem *child = children.at(ii);
1431 if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled)
1433 if (deliverWheelEvent(child, event))
1437 QPointF p = item->mapFromScene(event->posF());
1439 if (item->contains(p)) {
1440 QWheelEvent wheel(p, p, event->pixelDelta(), event->angleDelta(), event->delta(),
1441 event->orientation(), event->buttons(), event->modifiers());
1443 q->sendEvent(item, &wheel);
1444 if (wheel.isAccepted()) {
1453 #ifndef QT_NO_WHEELEVENT
1455 void QQuickWindow::wheelEvent(QWheelEvent *event)
1459 qWarning() << "QQuickWindow::wheelEvent()" << event->pixelDelta() << event->angleDelta();
1462 //if the actual wheel event was accepted, accept the compatibility wheel event and return early
1463 if (d->lastWheelEventAccepted && event->angleDelta().isNull())
1467 d->deliverWheelEvent(d->contentItem, event);
1468 d->lastWheelEventAccepted = event->isAccepted();
1470 #endif // QT_NO_WHEELEVENT
1473 bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event)
1476 qWarning("touchCancelEvent");
1479 // A TouchCancel event will typically not contain any points.
1480 // Deliver it to all items that have active touches.
1481 QSet<QQuickItem *> cancelDelivered;
1482 foreach (QQuickItem *item, itemForTouchPointId) {
1483 if (cancelDelivered.contains(item))
1485 cancelDelivered.insert(item);
1486 q->sendEvent(item, event);
1489 if (mouseGrabberItem)
1490 mouseGrabberItem->ungrabMouse();
1491 // The next touch event can only be a TouchBegin so clean up.
1492 itemForTouchPointId.clear();
1496 // check what kind of touch we have (begin/update) and
1497 // call deliverTouchPoints to actually dispatch the points
1498 bool QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event)
1501 if (event->type() == QEvent::TouchBegin)
1502 qWarning() << "touchBeginEvent";
1503 else if (event->type() == QEvent::TouchUpdate)
1504 qWarning() << "touchUpdateEvent points";
1505 else if (event->type() == QEvent::TouchEnd)
1506 qWarning("touchEndEvent");
1509 // List of all items that received an event before
1510 // When we have TouchBegin this is and will stay empty
1511 QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > updatedPoints;
1513 // Figure out who accepted a touch point last and put it in updatedPoints
1514 // Add additional item to newPoints
1515 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
1516 QList<QTouchEvent::TouchPoint> newPoints;
1517 for (int i=0; i<touchPoints.count(); i++) {
1518 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
1519 if (touchPoint.state() == Qt::TouchPointPressed) {
1520 newPoints << touchPoint;
1522 // TouchPointStationary is relevant only to items which
1523 // are also receiving touch points with some other state.
1524 // But we have not yet decided which points go to which item,
1525 // so for now we must include all non-new points in updatedPoints.
1526 if (itemForTouchPointId.contains(touchPoint.id())) {
1527 QQuickItem *item = itemForTouchPointId.value(touchPoint.id());
1529 updatedPoints[item].append(touchPoint);
1534 // Deliver the event, but only if there is at least one new point
1535 // or some item accepted a point and should receive an update
1536 if (newPoints.count() > 0 || updatedPoints.count() > 0) {
1537 QSet<int> acceptedNewPoints;
1538 event->setAccepted(deliverTouchPoints(contentItem, event, newPoints, &acceptedNewPoints, &updatedPoints));
1542 // Remove released points from itemForTouchPointId
1543 if (event->touchPointStates() & Qt::TouchPointReleased) {
1544 for (int i=0; i<touchPoints.count(); i++) {
1545 if (touchPoints[i].state() == Qt::TouchPointReleased) {
1546 itemForTouchPointId.remove(touchPoints[i].id());
1547 if (touchPoints[i].id() == touchMouseId)
1553 if (event->type() == QEvent::TouchEnd) {
1554 Q_ASSERT(itemForTouchPointId.isEmpty());
1557 return event->isAccepted();
1560 // This function recurses and sends the events to the individual items
1561 bool QQuickWindowPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints, QSet<int> *acceptedNewPoints, QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *updatedPoints)
1563 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1565 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1566 for (int i=0; i<newPoints.count(); i++) {
1567 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1568 if (!item->contains(p))
1573 // Check if our children want the event (or parts of it)
1574 // This is the only point where touch event delivery recurses!
1575 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1576 for (int ii = children.count() - 1; ii >= 0; --ii) {
1577 QQuickItem *child = children.at(ii);
1578 if (!child->isEnabled() || !child->isVisible() || QQuickItemPrivate::get(child)->culled)
1580 if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints))
1584 // None of the children accepted the event, so check the given item itself.
1585 // First, construct matchingPoints as a list of TouchPoints which the
1586 // given item might be interested in. Any newly-pressed point which is
1587 // inside the item's bounds will be interesting, and also any updated point
1588 // which was already accepted by that item when it was first pressed.
1589 // (A point which was already accepted is effectively "grabbed" by the item.)
1591 // set of IDs of "interesting" new points
1592 QSet<int> matchingNewPoints;
1593 // set of points which this item has previously accepted, for starters
1594 QList<QTouchEvent::TouchPoint> matchingPoints = (*updatedPoints)[item];
1595 // now add the new points which are inside this item's bounds
1596 if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) {
1597 for (int i = 0; i < newPoints.count(); i++) {
1598 if (acceptedNewPoints->contains(newPoints[i].id()))
1600 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1601 if (item->contains(p)) {
1602 matchingNewPoints.insert(newPoints[i].id());
1603 matchingPoints << newPoints[i];
1607 // If there are no matching new points, and the existing points are all stationary,
1608 // there's no need to send an event to this item. This is required by a test in
1609 // tst_qquickwindow::touchEvent_basic:
1610 // a single stationary press on an item shouldn't cause an event
1611 if (matchingNewPoints.isEmpty()) {
1612 bool stationaryOnly = true;
1613 Q_FOREACH (QTouchEvent::TouchPoint tp, matchingPoints)
1614 if (tp.state() != Qt::TouchPointStationary)
1615 stationaryOnly = false;
1617 matchingPoints.clear();
1620 if (!matchingPoints.isEmpty()) {
1621 // Now we know this item might be interested in the event. Copy and send it, but
1622 // with only the subset of TouchPoints which are relevant to that item: that's matchingPoints.
1623 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1624 transformTouchPoints(matchingPoints, itemPrivate->windowToItemTransform());
1625 deliverMatchingPointsToItem(item, event, acceptedNewPoints, matchingNewPoints, matchingPoints);
1628 // record the fact that this item has been visited already
1629 updatedPoints->remove(item);
1631 // recursion is done only if ALL touch points have been delivered
1632 return (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty());
1635 // touchEventForItemBounds has no means to generate a touch event that contains
1636 // only the points that are relevant for this item. Thus the need for
1637 // matchingPoints to already be that set of interesting points.
1638 // They are all pre-transformed, too.
1639 bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints)
1641 QScopedPointer<QTouchEvent> touchEvent(touchEventWithPoints(*event, matchingPoints));
1642 touchEvent.data()->setTarget(item);
1643 bool touchEventAccepted = false;
1645 // First check whether the parent wants to be a filter,
1646 // and if the parent accepts the event we are done.
1647 if (sendFilteredTouchEvent(item->parentItem(), item, event)) {
1652 // Since it can change in sendEvent, update itemForTouchPointId now
1653 foreach (int id, matchingNewPoints)
1654 itemForTouchPointId[id] = item;
1656 // Deliver the touch event to the given item
1657 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1658 itemPrivate->deliverTouchEvent(touchEvent.data());
1659 touchEventAccepted = touchEvent->isAccepted();
1661 // If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it.
1662 if (!touchEventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) {
1664 event->setAccepted(translateTouchToMouse(item, event));
1665 if (event->isAccepted()) {
1666 touchEventAccepted = true;
1670 if (touchEventAccepted) {
1671 // If the touch was accepted (regardless by whom or in what form),
1672 // update acceptedNewPoints.
1673 foreach (int id, matchingNewPoints)
1674 acceptedNewPoints->insert(id);
1676 // But if the event was not accepted then we know this item
1677 // will not be interested in further updates for those touchpoint IDs either.
1678 foreach (int id, matchingNewPoints)
1679 if (itemForTouchPointId[id] == item)
1680 itemForTouchPointId.remove(id);
1683 return touchEventAccepted;
1686 QTouchEvent *QQuickWindowPrivate::touchEventForItemBounds(QQuickItem *target, const QTouchEvent &originalEvent)
1688 const QList<QTouchEvent::TouchPoint> &touchPoints = originalEvent.touchPoints();
1689 QList<QTouchEvent::TouchPoint> pointsInBounds;
1690 // if all points are stationary, the list of points should be empty to signal a no-op
1691 if (originalEvent.touchPointStates() != Qt::TouchPointStationary) {
1692 for (int i = 0; i < touchPoints.count(); ++i) {
1693 const QTouchEvent::TouchPoint &tp = touchPoints.at(i);
1694 if (tp.state() == Qt::TouchPointPressed) {
1695 QPointF p = target->mapFromScene(tp.scenePos());
1696 if (target->contains(p))
1697 pointsInBounds.append(tp);
1699 pointsInBounds.append(tp);
1702 transformTouchPoints(pointsInBounds, QQuickItemPrivate::get(target)->windowToItemTransform());
1705 QTouchEvent* touchEvent = touchEventWithPoints(originalEvent, pointsInBounds);
1706 touchEvent->setTarget(target);
1710 QTouchEvent *QQuickWindowPrivate::touchEventWithPoints(const QTouchEvent &event, const QList<QTouchEvent::TouchPoint> &newPoints)
1712 Qt::TouchPointStates eventStates;
1713 for (int i=0; i<newPoints.count(); i++)
1714 eventStates |= newPoints[i].state();
1715 // if all points have the same state, set the event type accordingly
1716 QEvent::Type eventType = event.type();
1717 switch (eventStates) {
1718 case Qt::TouchPointPressed:
1719 eventType = QEvent::TouchBegin;
1721 case Qt::TouchPointReleased:
1722 eventType = QEvent::TouchEnd;
1725 eventType = QEvent::TouchUpdate;
1729 QTouchEvent *touchEvent = new QTouchEvent(eventType);
1730 touchEvent->setWindow(event.window());
1731 touchEvent->setTarget(event.target());
1732 touchEvent->setDevice(event.device());
1733 touchEvent->setModifiers(event.modifiers());
1734 touchEvent->setTouchPoints(newPoints);
1735 touchEvent->setTouchPointStates(eventStates);
1736 touchEvent->setTimestamp(event.timestamp());
1737 touchEvent->accept();
1741 #ifndef QT_NO_DRAGANDDROP
1742 void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event)
1745 grabber->resetTarget();
1746 QQuickDragGrabber::iterator grabItem = grabber->begin();
1747 if (grabItem != grabber->end()) {
1748 Q_ASSERT(event->type() != QEvent::DragEnter);
1749 if (event->type() == QEvent::Drop) {
1750 QDropEvent *e = static_cast<QDropEvent *>(event);
1751 for (e->setAccepted(false); !e->isAccepted() && grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1752 QPointF p = (**grabItem)->mapFromScene(e->pos());
1753 QDropEvent translatedEvent(
1755 e->possibleActions(),
1758 e->keyboardModifiers());
1759 QQuickDropEventEx::copyActions(&translatedEvent, *e);
1760 q->sendEvent(**grabItem, &translatedEvent);
1761 e->setAccepted(translatedEvent.isAccepted());
1762 e->setDropAction(translatedEvent.dropAction());
1763 grabber->setTarget(**grabItem);
1766 if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave.
1767 QDragLeaveEvent leaveEvent;
1768 for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
1769 q->sendEvent(**grabItem, &leaveEvent);
1771 } else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1772 QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event);
1773 if (deliverDragEvent(grabber, **grabItem, moveEvent)) {
1774 moveEvent->setAccepted(true);
1775 for (++grabItem; grabItem != grabber->end();) {
1776 QPointF p = (**grabItem)->mapFromScene(moveEvent->pos());
1777 if ((**grabItem)->contains(p)) {
1778 QDragMoveEvent translatedEvent(
1780 moveEvent->possibleActions(),
1781 moveEvent->mimeData(),
1782 moveEvent->mouseButtons(),
1783 moveEvent->keyboardModifiers());
1784 QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent);
1785 q->sendEvent(**grabItem, &translatedEvent);
1788 QDragLeaveEvent leaveEvent;
1789 q->sendEvent(**grabItem, &leaveEvent);
1790 grabItem = grabber->release(grabItem);
1795 QDragLeaveEvent leaveEvent;
1796 q->sendEvent(**grabItem, &leaveEvent);
1800 if (event->type() == QEvent::DragEnter || event->type() == QEvent::DragMove) {
1801 QDragMoveEvent *e = static_cast<QDragMoveEvent *>(event);
1802 QDragEnterEvent enterEvent(
1804 e->possibleActions(),
1807 e->keyboardModifiers());
1808 QQuickDropEventEx::copyActions(&enterEvent, *e);
1809 event->setAccepted(deliverDragEvent(grabber, contentItem, &enterEvent));
1813 bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event)
1816 bool accepted = false;
1817 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1818 if (!item->isVisible() || !item->isEnabled() || QQuickItemPrivate::get(item)->culled)
1821 QPointF p = item->mapFromScene(event->pos());
1822 if (item->contains(p)) {
1823 if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) {
1824 QDragMoveEvent translatedEvent(
1826 event->possibleActions(),
1828 event->mouseButtons(),
1829 event->keyboardModifiers(),
1831 QQuickDropEventEx::copyActions(&translatedEvent, *event);
1832 q->sendEvent(item, &translatedEvent);
1833 if (event->type() == QEvent::DragEnter) {
1834 if (translatedEvent.isAccepted()) {
1835 grabber->grab(item);
1842 } else if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1846 QDragEnterEvent enterEvent(
1848 event->possibleActions(),
1850 event->mouseButtons(),
1851 event->keyboardModifiers());
1852 QQuickDropEventEx::copyActions(&enterEvent, *event);
1853 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1854 for (int ii = children.count() - 1; ii >= 0; --ii) {
1855 if (deliverDragEvent(grabber, children.at(ii), &enterEvent))
1861 #endif // QT_NO_DRAGANDDROP
1863 #ifndef QT_NO_CURSOR
1864 void QQuickWindowPrivate::updateCursor(const QPointF &scenePos)
1868 QQuickItem *oldCursorItem = cursorItem;
1869 cursorItem = findCursorItem(contentItem, scenePos);
1871 if (cursorItem != oldCursorItem) {
1873 q->setCursor(cursorItem->cursor());
1879 QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF &scenePos)
1881 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1882 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1883 QPointF p = item->mapFromScene(scenePos);
1884 if (!item->contains(p))
1888 const int numCursorsInHierarchy = itemPrivate->extra.isAllocated() ? itemPrivate->extra.value().numItemsWithCursor : 0;
1889 const int numChildrenWithCursor = itemPrivate->hasCursor ? numCursorsInHierarchy-1 : numCursorsInHierarchy;
1891 if (numChildrenWithCursor > 0) {
1892 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1893 for (int ii = children.count() - 1; ii >= 0; --ii) {
1894 QQuickItem *child = children.at(ii);
1895 if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled)
1897 if (QQuickItem *cursorItem = findCursorItem(child, scenePos))
1902 if (itemPrivate->hasCursor) {
1903 QPointF p = item->mapFromScene(scenePos);
1904 if (item->contains(p))
1911 bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event)
1916 QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target);
1917 if (targetPrivate->filtersChildMouseEvents) {
1918 QScopedPointer<QTouchEvent> targetEvent(touchEventForItemBounds(target, *event));
1919 if (!targetEvent->touchPoints().isEmpty()) {
1920 QVector<int> touchIds;
1921 for (int i = 0; i < event->touchPoints().size(); ++i)
1922 touchIds.append(event->touchPoints().at(i).id());
1923 if (target->childMouseEventFilter(item, targetEvent.data())) {
1924 target->grabTouchPoints(touchIds);
1925 if (mouseGrabberItem) {
1926 mouseGrabberItem->ungrabMouse();
1932 // Only offer a mouse event to the filter if we have one point
1933 if (targetEvent->touchPoints().count() == 1) {
1935 const QTouchEvent::TouchPoint &tp = targetEvent->touchPoints().first();
1936 switch (tp.state()) {
1937 case Qt::TouchPointPressed:
1938 t = QEvent::MouseButtonPress;
1940 case Qt::TouchPointReleased:
1941 t = QEvent::MouseButtonRelease;
1944 // move or stationary
1945 t = QEvent::MouseMove;
1949 // targetEvent is already transformed wrt local position, velocity, etc.
1950 QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, targetEvent->touchPoints().first(), event, item, false));
1951 if (target->childMouseEventFilter(item, mouseEvent.data())) {
1952 itemForTouchPointId[tp.id()] = target;
1953 touchMouseId = tp.id();
1954 target->grabMouse();
1961 return sendFilteredTouchEvent(target->parentItem(), item, event);
1964 bool QQuickWindowPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QEvent *event)
1969 QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target);
1970 if (targetPrivate->filtersChildMouseEvents)
1971 if (target->childMouseEventFilter(item, event))
1974 if (sendFilteredMouseEvent(target->parentItem(), item, event))
1980 bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event)
1982 QStyleHints *styleHints = qApp->styleHints();
1983 int caps = QGuiApplicationPrivate::mouseEventCaps(event);
1984 bool dragVelocityLimitAvailable = (caps & QTouchDevice::Velocity)
1985 && styleHints->startDragVelocity();
1986 bool overThreshold = qAbs(d) > styleHints->startDragDistance();
1987 if (dragVelocityLimitAvailable) {
1988 QVector2D velocityVec = QGuiApplicationPrivate::mouseEventVelocity(event);
1989 qreal velocity = axis == Qt::XAxis ? velocityVec.x() : velocityVec.y();
1990 overThreshold |= qAbs(velocity) > styleHints->startDragVelocity();
1992 return overThreshold;
1995 bool QQuickWindowPrivate::isRenderable() const
1997 if (geometry.width() <= 0 || geometry.height() <= 0)
1999 // Change to be applied after the visibility property is integrated in qtbase:
2000 // return visibility != QWindow::Hidden || (renderWithoutShowing && platformWindow);
2001 // Temporary version which is implementation-agnostic but slightly less efficient:
2002 const QQuickWindow *q = q_func();
2003 return q->isVisible() || (renderWithoutShowing && platformWindow);
2007 Propagates an event \a e to a QQuickItem \a item on the window.
2009 The return value is currently not used.
2011 bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e)
2016 qWarning("QQuickWindow::sendEvent: Cannot send event to a null item");
2022 switch (e->type()) {
2023 case QEvent::KeyPress:
2024 case QEvent::KeyRelease:
2026 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
2027 while (!e->isAccepted() && (item = item->parentItem())) {
2029 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
2032 case QEvent::FocusIn:
2033 case QEvent::FocusOut:
2034 QQuickItemPrivate::get(item)->deliverFocusEvent(static_cast<QFocusEvent *>(e));
2036 case QEvent::MouseButtonPress:
2037 case QEvent::MouseButtonRelease:
2038 case QEvent::MouseButtonDblClick:
2039 case QEvent::MouseMove:
2040 // XXX todo - should sendEvent be doing this? how does it relate to forwarded events?
2041 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
2042 // accept because qml items by default accept and have to explicitly opt out of accepting
2044 QQuickItemPrivate::get(item)->deliverMouseEvent(static_cast<QMouseEvent *>(e));
2047 case QEvent::UngrabMouse:
2048 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
2050 item->mouseUngrabEvent();
2054 QQuickItemPrivate::get(item)->deliverWheelEvent(static_cast<QWheelEvent *>(e));
2056 case QEvent::HoverEnter:
2057 case QEvent::HoverLeave:
2058 case QEvent::HoverMove:
2059 QQuickItemPrivate::get(item)->deliverHoverEvent(static_cast<QHoverEvent *>(e));
2061 case QEvent::TouchBegin:
2062 case QEvent::TouchUpdate:
2063 case QEvent::TouchEnd:
2064 d->sendFilteredTouchEvent(item->parentItem(), item, static_cast<QTouchEvent *>(e));
2066 case QEvent::TouchCancel:
2067 QQuickItemPrivate::get(item)->deliverTouchEvent(static_cast<QTouchEvent *>(e));
2069 #ifndef QT_NO_DRAGANDDROP
2070 case QEvent::DragEnter:
2071 case QEvent::DragMove:
2072 case QEvent::DragLeave:
2074 QQuickItemPrivate::get(item)->deliverDragEvent(e);
2084 void QQuickWindowPrivate::cleanupNodes()
2086 for (int ii = 0; ii < cleanupNodeList.count(); ++ii)
2087 delete cleanupNodeList.at(ii);
2088 cleanupNodeList.clear();
2091 void QQuickWindowPrivate::cleanupNodesOnShutdown(QQuickItem *item)
2093 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
2094 if (p->itemNodeInstance) {
2095 delete p->itemNodeInstance;
2096 p->itemNodeInstance = 0;
2098 if (p->extra.isAllocated()) {
2099 p->extra->opacityNode = 0;
2100 p->extra->clipNode = 0;
2101 p->extra->rootNode = 0;
2108 for (int ii = 0; ii < p->childItems.count(); ++ii)
2109 cleanupNodesOnShutdown(p->childItems.at(ii));
2112 // This must be called from the render thread, with the main thread frozen
2113 void QQuickWindowPrivate::cleanupNodesOnShutdown()
2117 cleanupNodesOnShutdown(contentItem);
2118 QSet<QQuickItem *>::const_iterator it = parentlessItems.begin();
2119 for (; it != parentlessItems.end(); ++it)
2120 cleanupNodesOnShutdown(*it);
2121 q->cleanupSceneGraph();
2124 void QQuickWindowPrivate::updateDirtyNodes()
2127 qWarning() << "QQuickWindowPrivate::updateDirtyNodes():";
2132 QQuickItem *updateList = dirtyItemList;
2134 if (updateList) QQuickItemPrivate::get(updateList)->prevDirtyItem = &updateList;
2136 while (updateList) {
2137 QQuickItem *item = updateList;
2138 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
2139 itemPriv->removeFromDirtyList();
2142 qWarning() << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString());
2144 updateDirtyNode(item);
2148 void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item)
2150 #ifdef QML_RUNTIME_TESTING
2151 bool didFlash = false;
2154 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
2155 quint32 dirty = itemPriv->dirtyAttributes;
2156 itemPriv->dirtyAttributes = 0;
2158 if ((dirty & QQuickItemPrivate::TransformUpdateMask) ||
2159 (dirty & QQuickItemPrivate::Size && itemPriv->origin() != QQuickItem::TopLeft &&
2160 (itemPriv->scale() != 1. || itemPriv->rotation() != 0.))) {
2164 if (itemPriv->x != 0. || itemPriv->y != 0.)
2165 matrix.translate(itemPriv->x, itemPriv->y);
2167 for (int ii = itemPriv->transforms.count() - 1; ii >= 0; --ii)
2168 itemPriv->transforms.at(ii)->applyTo(&matrix);
2170 if (itemPriv->scale() != 1. || itemPriv->rotation() != 0.) {
2171 QPointF origin = item->transformOriginPoint();
2172 matrix.translate(origin.x(), origin.y());
2173 if (itemPriv->scale() != 1.)
2174 matrix.scale(itemPriv->scale(), itemPriv->scale());
2175 if (itemPriv->rotation() != 0.)
2176 matrix.rotate(itemPriv->rotation(), 0, 0, 1);
2177 matrix.translate(-origin.x(), -origin.y());
2180 itemPriv->itemNode()->setMatrix(matrix);
2183 bool clipEffectivelyChanged = (dirty & (QQuickItemPrivate::Clip | QQuickItemPrivate::Window)) &&
2184 ((item->clip() == false) != (itemPriv->clipNode() == 0));
2185 int effectRefCount = itemPriv->extra.isAllocated()?itemPriv->extra->effectRefCount:0;
2186 bool effectRefEffectivelyChanged = (dirty & (QQuickItemPrivate::EffectReference | QQuickItemPrivate::Window)) &&
2187 ((effectRefCount == 0) != (itemPriv->rootNode() == 0));
2189 if (clipEffectivelyChanged) {
2190 QSGNode *parent = itemPriv->opacityNode() ? (QSGNode *) itemPriv->opacityNode() :
2191 (QSGNode *)itemPriv->itemNode();
2192 QSGNode *child = itemPriv->rootNode() ? (QSGNode *)itemPriv->rootNode() :
2193 (QSGNode *)itemPriv->groupNode;
2196 Q_ASSERT(itemPriv->clipNode() == 0);
2197 itemPriv->extra.value().clipNode = new QQuickDefaultClipNode(item->clipRect());
2198 itemPriv->clipNode()->update();
2201 parent->removeChildNode(child);
2202 parent->appendChildNode(itemPriv->clipNode());
2204 itemPriv->clipNode()->appendChildNode(child);
2207 Q_ASSERT(itemPriv->clipNode() != 0);
2208 parent->removeChildNode(itemPriv->clipNode());
2210 itemPriv->clipNode()->removeChildNode(child);
2211 delete itemPriv->clipNode();
2212 itemPriv->extra->clipNode = 0;
2214 parent->appendChildNode(child);
2218 if (dirty & QQuickItemPrivate::ChildrenUpdateMask)
2219 itemPriv->childContainerNode()->removeAllChildNodes();
2221 if (effectRefEffectivelyChanged) {
2222 QSGNode *parent = itemPriv->clipNode();
2224 parent = itemPriv->opacityNode();
2226 parent = itemPriv->itemNode();
2227 QSGNode *child = itemPriv->groupNode;
2229 if (itemPriv->extra.isAllocated() && itemPriv->extra->effectRefCount) {
2230 Q_ASSERT(itemPriv->rootNode() == 0);
2231 itemPriv->extra->rootNode = new QSGRootNode;
2234 parent->removeChildNode(child);
2235 parent->appendChildNode(itemPriv->rootNode());
2237 itemPriv->rootNode()->appendChildNode(child);
2239 Q_ASSERT(itemPriv->rootNode() != 0);
2240 parent->removeChildNode(itemPriv->rootNode());
2242 itemPriv->rootNode()->removeChildNode(child);
2243 delete itemPriv->rootNode();
2244 itemPriv->extra->rootNode = 0;
2246 parent->appendChildNode(child);
2250 if (dirty & QQuickItemPrivate::ChildrenUpdateMask) {
2251 QSGNode *groupNode = itemPriv->groupNode;
2253 groupNode->removeAllChildNodes();
2255 QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems();
2258 for (; ii < orderedChildren.count() && orderedChildren.at(ii)->z() < 0; ++ii) {
2259 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
2260 if (!childPrivate->explicitVisible &&
2261 (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount))
2263 if (childPrivate->itemNode()->parent())
2264 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
2266 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
2269 QSGNode *beforePaintNode = itemPriv->groupNode ? itemPriv->groupNode->lastChild() : 0;
2270 if (beforePaintNode || itemPriv->extra.isAllocated())
2271 itemPriv->extra.value().beforePaintNode = beforePaintNode;
2273 if (itemPriv->paintNode)
2274 itemPriv->childContainerNode()->appendChildNode(itemPriv->paintNode);
2276 for (; ii < orderedChildren.count(); ++ii) {
2277 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
2278 if (!childPrivate->explicitVisible &&
2279 (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount))
2281 if (childPrivate->itemNode()->parent())
2282 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
2284 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
2288 if ((dirty & QQuickItemPrivate::Size) && itemPriv->clipNode()) {
2289 itemPriv->clipNode()->setRect(item->clipRect());
2290 itemPriv->clipNode()->update();
2293 if (dirty & (QQuickItemPrivate::OpacityValue | QQuickItemPrivate::Visible
2294 | QQuickItemPrivate::HideReference | QQuickItemPrivate::Window))
2296 qreal opacity = itemPriv->explicitVisible && (!itemPriv->extra.isAllocated() || itemPriv->extra->hideRefCount == 0)
2297 ? itemPriv->opacity() : qreal(0);
2299 if (opacity != 1 && !itemPriv->opacityNode()) {
2300 itemPriv->extra.value().opacityNode = new QSGOpacityNode;
2302 QSGNode *parent = itemPriv->itemNode();
2303 QSGNode *child = itemPriv->clipNode();
2305 child = itemPriv->rootNode();
2307 child = itemPriv->groupNode;
2310 parent->removeChildNode(child);
2311 parent->appendChildNode(itemPriv->opacityNode());
2313 itemPriv->opacityNode()->appendChildNode(child);
2315 if (itemPriv->opacityNode())
2316 itemPriv->opacityNode()->setOpacity(opacity);
2319 if (dirty & QQuickItemPrivate::ContentUpdateMask) {
2321 if (itemPriv->flags & QQuickItem::ItemHasContents) {
2322 updatePaintNodeData.transformNode = itemPriv->itemNode();
2323 itemPriv->paintNode = item->updatePaintNode(itemPriv->paintNode, &updatePaintNodeData);
2325 Q_ASSERT(itemPriv->paintNode == 0 ||
2326 itemPriv->paintNode->parent() == 0 ||
2327 itemPriv->paintNode->parent() == itemPriv->childContainerNode());
2329 if (itemPriv->paintNode && itemPriv->paintNode->parent() == 0) {
2330 if (itemPriv->extra.isAllocated() && itemPriv->extra->beforePaintNode)
2331 itemPriv->childContainerNode()->insertChildNodeAfter(itemPriv->paintNode, itemPriv->extra->beforePaintNode);
2333 itemPriv->childContainerNode()->prependChildNode(itemPriv->paintNode);
2335 } else if (itemPriv->paintNode) {
2336 delete itemPriv->paintNode;
2337 itemPriv->paintNode = 0;
2342 // Check consistency.
2343 const QSGNode *nodeChain[] = {
2344 itemPriv->itemNodeInstance,
2345 itemPriv->opacityNode(),
2346 itemPriv->clipNode(),
2347 itemPriv->rootNode(),
2348 itemPriv->groupNode,
2349 itemPriv->paintNode,
2354 while (ip < 5 && nodeChain[ip] == 0)
2359 while (ic < 5 && nodeChain[ic] == 0)
2361 const QSGNode *parent = nodeChain[ip];
2362 const QSGNode *child = nodeChain[ic];
2364 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 0);
2366 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 1);
2367 Q_ASSERT(child->parent() == parent);
2368 bool containsChild = false;
2369 for (QSGNode *n = parent->firstChild(); n; n = n->nextSibling())
2370 containsChild |= (n == child);
2371 Q_ASSERT(containsChild);
2377 #ifdef QML_RUNTIME_TESTING
2378 if (itemPriv->sceneGraphContext()->isFlashModeEnabled()) {
2379 QSGFlashNode *flash = new QSGFlashNode();
2380 flash->setRect(item->boundingRect());
2381 itemPriv->childContainerNode()->appendChildNode(flash);
2392 void QQuickWindow::maybeUpdate()
2395 d->windowManager->maybeUpdate(this);
2398 void QQuickWindow::cleanupSceneGraph()
2405 delete d->renderer->rootNode();
2412 Returns the opengl context used for rendering.
2414 If the scene graph is not ready, this function will return 0.
2416 \sa sceneGraphInitialized(), sceneGraphInvalidated()
2419 QOpenGLContext *QQuickWindow::openglContext() const
2421 Q_D(const QQuickWindow);
2422 if (d->context->isReady())
2423 return d->context->glContext();
2428 \fn void QQuickWindow::frameSwapped()
2430 This signal is emitted when the frame buffers have been swapped.
2432 This signal will be emitted from the scene graph rendering thread.
2437 \fn void QQuickWindow::sceneGraphInitialized()
2439 This signal is emitted when the scene graph has been initialized.
2441 This signal will be emitted from the scene graph rendering thread.
2447 \fn void QQuickWindow::sceneGraphInvalidated()
2449 This signal is emitted when the scene graph has been invalidated.
2451 This signal implies that the opengl rendering context used
2452 has been invalidated and all user resources tied to that context
2455 This signal will be emitted from the scene graph rendering thread.
2460 Sets the render target for this window to be \a fbo.
2462 The specified fbo must be created in the context of the window
2463 or one that shares with it.
2466 This function can only be called from the thread doing
2470 void QQuickWindow::setRenderTarget(QOpenGLFramebufferObject *fbo)
2473 if (d->context && QThread::currentThread() != d->context->thread()) {
2474 qWarning("QQuickWindow::setRenderThread: Cannot set render target from outside the rendering thread");
2478 d->renderTarget = fbo;
2480 d->renderTargetId = fbo->handle();
2481 d->renderTargetSize = fbo->size();
2483 d->renderTargetId = 0;
2484 d->renderTargetSize = QSize();
2491 Sets the render target for this window to be an FBO with
2492 \a fboId and \a size.
2494 The specified FBO must be created in the context of the window
2495 or one that shares with it.
2498 This function can only be called from the thread doing
2502 void QQuickWindow::setRenderTarget(uint fboId, const QSize &size)
2505 if (d->context && QThread::currentThread() != d->context->thread()) {
2506 qWarning("QQuickWindow::setRenderThread: Cannot set render target from outside the rendering thread");
2510 d->renderTargetId = fboId;
2511 d->renderTargetSize = size;
2513 // Unset any previously set instance...
2514 d->renderTarget = 0;
2519 Returns the FBO id of the render target when set; otherwise returns 0.
2521 uint QQuickWindow::renderTargetId() const
2523 Q_D(const QQuickWindow);
2524 return d->renderTargetId;
2528 Returns the size of the currently set render target; otherwise returns an empty size.
2530 QSize QQuickWindow::renderTargetSize() const
2532 Q_D(const QQuickWindow);
2533 return d->renderTargetSize;
2540 Returns the render target for this window.
2542 The default is to render to the surface of the window, in which
2543 case the render target is 0.
2545 QOpenGLFramebufferObject *QQuickWindow::renderTarget() const
2547 Q_D(const QQuickWindow);
2548 return d->renderTarget;
2553 Grabs the contents of the window and returns it as an image.
2555 This function might not work if the window is not visible.
2557 \warning Calling this function will cause performance problems.
2559 \warning This function can only be called from the GUI thread.
2561 QImage QQuickWindow::grabWindow()
2564 return d->windowManager->grab(this);
2568 Returns an incubation controller that splices incubation between frames
2569 for this window. QQuickView automatically installs this controller for you,
2570 otherwise you will need to install it yourself using \l{QQmlEngine::setIncubationController()}.
2572 The controller is owned by the window and will be destroyed when the window
2575 QQmlIncubationController *QQuickWindow::incubationController() const
2577 Q_D(const QQuickWindow);
2579 if (!d->incubationController)
2580 d->incubationController = new QQuickWindowIncubationController(this);
2581 return d->incubationController;
2587 \enum QQuickWindow::CreateTextureOption
2589 The CreateTextureOption enums are used to customize a texture is wrapped.
2591 \value TextureHasAlphaChannel The texture has an alpha channel and should
2592 be drawn using blending.
2594 \value TextureHasMipmaps The texture has mipmaps and can be drawn with
2597 \value TextureOwnsGLTexture The texture object owns the texture id and
2598 will delete the GL texture when the texture object is deleted.
2602 \fn void QQuickWindow::beforeSynchronizing()
2604 This signal is emitted before the scene graph is synchronized with the QML state.
2606 This signal can be used to do any preparation required before calls to
2607 QQuickItem::updatePaintNode().
2609 The GL context used for rendering the scene graph will be bound at this point.
2611 \warning This signal is emitted from the scene graph rendering thread. If your
2612 slot function needs to finish before execution continues, you must make sure that
2613 the connection is direct (see Qt::ConnectionType).
2615 \warning Make very sure that a signal handler for beforeSynchronizing leaves the GL
2616 context in the same state as it was when the signal handler was entered. Failing to
2617 do so can result in the scene not rendering properly.
2621 \fn void QQuickWindow::beforeRendering()
2623 This signal is emitted before the scene starts rendering.
2625 Combined with the modes for clearing the background, this option
2626 can be used to paint using raw GL under QML content.
2628 The GL context used for rendering the scene graph will be bound
2631 \warning This signal is emitted from the scene graph rendering thread. If your
2632 slot function needs to finish before execution continues, you must make sure that
2633 the connection is direct (see Qt::ConnectionType).
2635 \warning Make very sure that a signal handler for beforeRendering leaves the GL
2636 context in the same state as it was when the signal handler was entered. Failing to
2637 do so can result in the scene not rendering properly.
2641 \fn void QQuickWindow::afterRendering()
2643 This signal is emitted after the scene has completed rendering, before swapbuffers is called.
2645 This signal can be used to paint using raw GL on top of QML content,
2646 or to do screen scraping of the current frame buffer.
2648 The GL context used for rendering the scene graph will be bound at this point.
2650 \warning This signal is emitted from the scene graph rendering thread. If your
2651 slot function needs to finish before execution continues, you must make sure that
2652 the connection is direct (see Qt::ConnectionType).
2654 \warning Make very sure that a signal handler for afterRendering() leaves the GL
2655 context in the same state as it was when the signal handler was entered. Failing to
2656 do so can result in the scene not rendering properly.
2662 Sets whether the scene graph rendering of QML should clear the color buffer
2663 before it starts rendering to \a enabled.
2665 By disabling clearing of the color buffer, it is possible to do GL painting
2666 under the scene graph.
2668 The color buffer is cleared by default.
2670 \sa beforeRendering()
2673 void QQuickWindow::setClearBeforeRendering(bool enabled)
2676 d->clearBeforeRendering = enabled;
2682 Returns whether clearing of the color buffer is done before rendering or not.
2685 bool QQuickWindow::clearBeforeRendering() const
2687 Q_D(const QQuickWindow);
2688 return d->clearBeforeRendering;
2694 Creates a new QSGTexture from the supplied \a image. If the image has an
2695 alpha channel, the corresponding texture will have an alpha channel.
2697 The caller of the function is responsible for deleting the returned texture.
2698 The actual GL texture will be deleted when the texture object is deleted.
2700 Depending on the underlying implementation of the scene graph, the returned
2701 texture may be part of an atlas. For code to be portable across implementations
2702 one should always use the texture coordinates returned from
2703 QSGTexture::normalizedTextureSubRect() when building geometry.
2705 \warning This function will return 0 if the scene graph has not yet been
2708 \warning The returned texture is not memory managed by the scene graph and
2709 must be explicitly deleted by the caller on the rendering thread.
2710 This is acheived by deleting the texture from a QSGNode destructor
2711 or by using deleteLater() in the case where the texture already has affinity
2712 to the rendering thread.
2714 This function can be called from any thread.
2716 \sa sceneGraphInitialized()
2719 QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const
2721 Q_D(const QQuickWindow);
2722 if (d->context && d->context->isReady())
2723 return d->context->createTexture(image);
2731 Creates a new QSGTexture object from an existing GL texture \a id and \a size.
2733 The caller of the function is responsible for deleting the returned texture.
2735 Use \a options to customize the texture attributes.
2737 \warning This function will return 0 if the scenegraph has not yet been
2740 \sa sceneGraphInitialized()
2742 QSGTexture *QQuickWindow::createTextureFromId(uint id, const QSize &size, CreateTextureOptions options) const
2744 Q_D(const QQuickWindow);
2745 if (d->context && d->context->isReady()) {
2746 QSGPlainTexture *texture = new QSGPlainTexture();
2747 texture->setTextureId(id);
2748 texture->setHasAlphaChannel(options & TextureHasAlphaChannel);
2749 texture->setHasMipmaps(options & TextureHasMipmaps);
2750 texture->setOwnsTexture(options & TextureOwnsGLTexture);
2751 texture->setTextureSize(size);
2758 \qmlproperty color QtQuick.Window2::Window::color
2760 The background color for the window.
2762 Setting this property is more efficient than using a separate Rectangle.
2766 \property QQuickWindow::color
2767 \brief The color used to clear the OpenGL context.
2769 Setting the clear color has no effect when clearing is disabled.
2770 By default, the clear color is white.
2772 \sa setClearBeforeRendering()
2775 void QQuickWindow::setColor(const QColor &color)
2778 if (color == d->clearColor)
2781 d->clearColor = color;
2782 emit colorChanged(color);
2783 d->dirtyItem(contentItem());
2786 QColor QQuickWindow::color() const
2788 return d_func()->clearColor;
2792 \qmlproperty string QtQuick.Window2::Window::title
2794 The window's title in the windowing system.
2796 The window title might appear in the title area of the window decorations,
2797 depending on the windowing system and the window flags. It might also
2798 be used by the windowing system to identify the window in other contexts,
2799 such as in the task switcher.
2803 \qmlproperty string QtQuick.Window2::Window::modality
2805 The modality of the window.
2807 A modal window prevents other windows from receiving input events.
2808 Possible values are Qt.NonModal (the default), Qt.WindowModal,
2809 and Qt.ApplicationModal.
2812 #include "moc_qquickwindow.cpp"