qquickwindow.cpp 134 KB
Newer Older
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
** This file is part of the QtQuick module of the Qt Toolkit.
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/

Alan Alpert's avatar
Alan Alpert committed
#include "qquickwindow.h"
#include "qquickwindow_p.h"
#include "qquickwindowattached_p.h"
#include "qquickitem.h"
#include "qquickitem_p.h"
#include "qquickevents_p_p.h"
#include <private/qquickdrag_p.h>

#include <QtQuick/private/qsgrenderer_p.h>
#include <QtQuick/private/qsgtexture_p.h>
#include <private/qsgrenderloop_p.h>
Lars Knoll's avatar
Lars Knoll committed
#include <private/qquickrendercontrol_p.h>
#include <private/qquickanimatorcontroller_p.h>
#include <private/qguiapplication_p.h>
#include <QtGui/QInputMethod>
#include <private/qabstractanimation_p.h>

#include <QtGui/qpainter.h>
#include <QtGui/qevent.h>
#include <QtGui/qmatrix4x4.h>
#include <QtGui/qstylehints.h>
#include <QtCore/qvarlengtharray.h>
#include <QtCore/qabstractanimation.h>
#include <QtCore/QLibraryInfo>
#include <QtCore/QRunnable>
#include <QtQml/qqmlincubator.h>
#include <QtQuick/private/qquickpixmapcache_p.h>
#include <private/qqmlprofilerservice_p.h>
#include <private/qqmlmemoryprofiler_p.h>
#include <private/qopenglvertexarrayobject_p.h>

QT_BEGIN_NAMESPACE

Q_LOGGING_CATEGORY(DBG_TOUCH, "qt.quick.touch");
Q_LOGGING_CATEGORY(DBG_MOUSE, "qt.quick.mouse");
Q_LOGGING_CATEGORY(DBG_FOCUS, "qt.quick.focus");
Q_LOGGING_CATEGORY(DBG_DIRTY, "qt.quick.dirty");

extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);

bool QQuickWindowPrivate::defaultAlphaBuffer = false;
Alan Alpert's avatar
Alan Alpert committed
void QQuickWindowPrivate::updateFocusItemTransform()
Alan Alpert's avatar
Alan Alpert committed
    Q_Q(QQuickWindow);
#ifndef QT_NO_IM
    QQuickItem *focus = q->activeFocusItem();
    if (focus && qApp->focusObject() == focus) {
        QQuickItemPrivate *focusPrivate = QQuickItemPrivate::get(focus);
        qApp->inputMethod()->setInputItemTransform(focusPrivate->itemToWindowTransform());
        qApp->inputMethod()->setInputItemRectangle(QRectF(0, 0, focusPrivate->width, focusPrivate->height));
    }
Alan Alpert's avatar
Alan Alpert committed
class QQuickWindowIncubationController : public QObject, public QQmlIncubationController
    QQuickWindowIncubationController(QSGRenderLoop *loop)
        : m_renderLoop(loop), m_timer(0)
    {
        // Allow incubation for 1/3 of a frame.
        m_incubation_time = qMax(1, int(1000 / QGuiApplication::primaryScreen()->refreshRate()) / 3);

        QAnimationDriver *animationDriver = m_renderLoop->animationDriver();
        if (animationDriver) {
            connect(animationDriver, SIGNAL(stopped()), this, SLOT(animationStopped()));
            connect(m_renderLoop, SIGNAL(timeToIncubate()), this, SLOT(incubate()));
    void timerEvent(QTimerEvent *)
        killTimer(m_timer);
        m_timer = 0;
        incubate();
    }

    void incubateAgain() {
        if (m_timer == 0) {
            // Wait for a while before processing the next batch. Using a
            // timer to avoid starvation of system events.
            m_timer = startTimer(m_incubation_time);
public slots:
    void incubate() {
        if (incubatingObjectCount()) {
            if (m_renderLoop->interleaveIncubation()) {
                incubateFor(m_incubation_time);
            } else {
                incubateFor(m_incubation_time * 2);
                if (incubatingObjectCount())
            }
        }
    }

    void animationStopped() { incubate(); }

protected:
    virtual void incubatingObjectCountChanged(int count)
    {
        if (count && !m_renderLoop->interleaveIncubation())
            incubateAgain();
#ifndef QT_NO_ACCESSIBILITY
/*!
    Returns an accessibility interface for this window, or 0 if such an
    interface cannot be created.
*/
Alan Alpert's avatar
Alan Alpert committed
QAccessibleInterface *QQuickWindow::accessibleRoot() const
Alan Alpert's avatar
Alan Alpert committed
    return QAccessible::queryAccessibleInterface(const_cast<QQuickWindow*>(this));
/*
Focus behavior
==============

Alan Alpert's avatar
Alan Alpert committed
Prior to being added to a valid window items can set and clear focus with no
effect.  Only once items are added to a window (by way of having a parent set that
already belongs to a window) do the focus rules apply.  Focus goes back to
having no effect if an item is removed from a window.
Alan Alpert's avatar
Alan Alpert committed
When an item is moved into a new focus scope (either being added to a window
for the first time, or having its parent changed), if the focus scope already has
a scope focused item that takes precedence over the item being added.  Otherwise,
the focus of the added tree is used.  In the case of a tree of items being
Alan Alpert's avatar
Alan Alpert committed
added to a window for the first time, which may have a conflicted focus state (two
or more items in one scope having focus set), the same rule is applied item by item -
thus the first item that has focus will get it (assuming the scope doesn't already
have a scope focused item), and the other items will have their focus cleared.
*/

QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData()
: transformNode(0)
{
}

QQuickRootItem::QQuickRootItem()
/*! \reimp */
Alan Alpert's avatar
Alan Alpert committed
void QQuickWindow::exposeEvent(QExposeEvent *)
Alan Alpert's avatar
Alan Alpert committed
    Q_D(QQuickWindow);
Lars Knoll's avatar
Lars Knoll committed
    if (d->windowManager)
        d->windowManager->exposureChanged(this);
/*! \reimp */
void QQuickWindow::resizeEvent(QResizeEvent *ev)
Lars Knoll's avatar
Lars Knoll committed
    Q_D(QQuickWindow);
    if (d->contentItem)
        d->contentItem->setSize(ev->size());
Lars Knoll's avatar
Lars Knoll committed
    if (d->windowManager)
        d->windowManager->resize(this);
/*! \reimp */
Alan Alpert's avatar
Alan Alpert committed
void QQuickWindow::showEvent(QShowEvent *)
Lars Knoll's avatar
Lars Knoll committed
    Q_D(QQuickWindow);
    if (d->windowManager)
        d->windowManager->show(this);
/*! \reimp */
Alan Alpert's avatar
Alan Alpert committed
void QQuickWindow::hideEvent(QHideEvent *)
Lars Knoll's avatar
Lars Knoll committed
    Q_D(QQuickWindow);
    if (d->windowManager)
        d->windowManager->hide(this);
/*! \reimp */
Caroline Chao's avatar
Caroline Chao committed
void QQuickWindow::focusOutEvent(QFocusEvent *ev)
Alan Alpert's avatar
Alan Alpert committed
    Q_D(QQuickWindow);
Caroline Chao's avatar
Caroline Chao committed
    d->contentItem->setFocus(false, ev->reason());
/*! \reimp */
Caroline Chao's avatar
Caroline Chao committed
void QQuickWindow::focusInEvent(QFocusEvent *ev)
Alan Alpert's avatar
Alan Alpert committed
    Q_D(QQuickWindow);
Caroline Chao's avatar
Caroline Chao committed
    d->contentItem->setFocus(true, ev->reason());
Alan Alpert's avatar
Alan Alpert committed
void QQuickWindowPrivate::polishItems()
    int maxPolishCycles = 100000;
J-P Nurmi's avatar
J-P Nurmi committed
    while (!itemsToPolish.isEmpty() && --maxPolishCycles > 0) {
        QSet<QQuickItem *> itms = itemsToPolish;
J-P Nurmi's avatar
J-P Nurmi committed
        itemsToPolish.clear();

        for (QSet<QQuickItem *>::iterator it = itms.begin(); it != itms.end(); ++it) {
            QQuickItem *item = *it;
J-P Nurmi's avatar
J-P Nurmi committed
            QQuickItemPrivate::get(item)->polishScheduled = false;
            item->updatePolish();
J-P Nurmi's avatar
J-P Nurmi committed
    }
Alan Alpert's avatar
Alan Alpert committed
        qWarning("QQuickWindow: possible QQuickItem::polish() loop");
    updateFocusItemTransform();
Alan Alpert's avatar
Alan Alpert committed
 * Schedules the window to render another frame.
Alan Alpert's avatar
Alan Alpert committed
 * Calling QQuickWindow::update() differs from QQuickItem::update() in that
 * it always triggers a repaint, regardless of changes in the underlying
 * scene graph or not.
 */
Alan Alpert's avatar
Alan Alpert committed
void QQuickWindow::update()
Alan Alpert's avatar
Alan Alpert committed
    Q_D(QQuickWindow);
Lars Knoll's avatar
Lars Knoll committed
    if (d->windowManager)
        d->windowManager->update(this);
    else if (d->renderControl)
        QQuickRenderControlPrivate::get(d->renderControl)->update();
void forcePolishHelper(QQuickItem *item)
{
    if (item->flags() & QQuickItem::ItemHasContents) {
        item->polish();
    }

    QList <QQuickItem *> items = item->childItems();
    for (int i=0; i<items.size(); ++i)
        forcePolishHelper(items.at(i));
}

/*!
    Schedules polish events on all items in the scene.
*/
void QQuickWindow::forcePolish()
{
    Q_D(QQuickWindow);
    if (!screen())
        return;
    forcePolishHelper(d->contentItem);
}

void forceUpdate(QQuickItem *item)
{
    if (item->flags() & QQuickItem::ItemHasContents)
        item->update();
    QQuickItemPrivate::get(item)->dirty(QQuickItemPrivate::ChildrenUpdateMask);

    QList <QQuickItem *> items = item->childItems();
    for (int i=0; i<items.size(); ++i)
        forceUpdate(items.at(i));
}
Alan Alpert's avatar
Alan Alpert committed
void QQuickWindowPrivate::syncSceneGraph()
    QML_MEMORY_SCOPE_STRING("SceneGraph");
Alan Alpert's avatar
Alan Alpert committed
    Q_Q(QQuickWindow);
    animationController->beforeNodeSync();

    runAndClearJobs(&beforeSynchronizingJobs);
        forceUpdate(contentItem);
        QSGRootNode *rootNode = new QSGRootNode;
        rootNode->appendChildNode(QQuickItemPrivate::get(contentItem)->itemNode());
        renderer = context->createRenderer();
        renderer->setRootNode(rootNode);
    }

    updateDirtyNodes();
    animationController->afterNodeSync();

Alan Alpert's avatar
Alan Alpert committed
    // Copy the current state of clearing from window into renderer.
    renderer->setClearColor(clearColor);
    QSGAbstractRenderer::ClearMode mode = QSGAbstractRenderer::ClearStencilBuffer | QSGAbstractRenderer::ClearDepthBuffer;
    if (clearBeforeRendering)
        mode |= QSGAbstractRenderer::ClearColorBuffer;
    renderer->setClearMode(mode);
    renderer->setCustomRenderMode(customRenderMode);

    emit q->afterSynchronizing();
    runAndClearJobs(&afterSynchronizingJobs);
Alan Alpert's avatar
Alan Alpert committed
void QQuickWindowPrivate::renderSceneGraph(const QSize &size)
    QML_MEMORY_SCOPE_STRING("SceneGraph");
Alan Alpert's avatar
Alan Alpert committed
    Q_Q(QQuickWindow);
    animationController->advance();
    runAndClearJobs(&beforeRenderingJobs);
    const qreal devicePixelRatio = q->effectiveDevicePixelRatio();
    renderer->setDeviceRect(QRect(QPoint(0, 0), size * devicePixelRatio));
    if (renderTargetId) {
        fboId = renderTargetId;
        renderer->setViewportRect(QRect(QPoint(0, 0), renderTargetSize));
    } else {
        renderer->setViewportRect(QRect(QPoint(0, 0), size * devicePixelRatio));
    renderer->setProjectionMatrixToRect(QRect(QPoint(0, 0), size));
    renderer->setDevicePixelRatio(devicePixelRatio);
    context->renderNextFrame(renderer, fboId);
    emit q->afterRendering();
    runAndClearJobs(&afterRenderingJobs);
Alan Alpert's avatar
Alan Alpert committed
QQuickWindowPrivate::QQuickWindowPrivate()
    : contentItem(0)
    , activeFocusItem(0)
    , mouseGrabberItem(0)
#ifndef QT_NO_CURSOR
    , cursorItem(0)
#endif
#ifndef QT_NO_DRAGANDDROP
    , dragGrabber(0)
    , touchMouseId(-1)
    , touchMousePressTimestamp(0)
    , dirtyItemList(0)
    , context(0)
    , renderer(0)
    , windowManager(0)
Lars Knoll's avatar
Lars Knoll committed
    , renderControl(0)
    , touchRecursionGuard(0)
    , clearColor(Qt::white)
    , clearBeforeRendering(true)
    , persistentGLContext(true)
    , persistentSceneGraph(true)
    , lastWheelEventAccepted(false)
    , lastFocusReason(Qt::OtherFocusReason)
    , incubationController(0)
#ifndef QT_NO_DRAGANDDROP
    dragGrabber = new QQuickDragGrabber;
#endif
Alan Alpert's avatar
Alan Alpert committed
QQuickWindowPrivate::~QQuickWindowPrivate()
Lars Knoll's avatar
Lars Knoll committed
void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control)
Alan Alpert's avatar
Alan Alpert committed
    Q_Q(QQuickWindow);
    contentItem = new QQuickRootItem;
    QQmlEngine::setObjectOwnership(contentItem, QQmlEngine::CppOwnership);
    QQuickItemPrivate *contentItemPrivate = QQuickItemPrivate::get(contentItem);
    contentItemPrivate->window = q;
    contentItemPrivate->windowRefCount = 1;
    contentItemPrivate->flags |= QQuickItem::ItemIsFocusScope;
    contentItem->setSize(q->size());
    customRenderMode = qgetenv("QSG_VISUALIZE");
Lars Knoll's avatar
Lars Knoll committed
    renderControl = control;
    if (renderControl)
        QQuickRenderControlPrivate::get(renderControl)->window = q;
Lars Knoll's avatar
Lars Knoll committed

    if (!renderControl)
        windowManager = QSGRenderLoop::instance();

    Q_ASSERT(windowManager || renderControl);

    QSGContext *sg;
    if (renderControl) {
        QQuickRenderControlPrivate *renderControlPriv = QQuickRenderControlPrivate::get(renderControl);
        sg = renderControlPriv->sg;
        context = renderControlPriv->rc;
Lars Knoll's avatar
Lars Knoll committed
    } else {
        windowManager->addWindow(q);
Lars Knoll's avatar
Lars Knoll committed
        sg = windowManager->sceneGraphContext();
        context = windowManager->createRenderContext(sg);
    }

    q->setSurfaceType(windowManager ? windowManager->windowSurfaceType() : QSurface::OpenGLSurface);
    q->setFormat(sg->defaultSurfaceFormat());
    animationController = new QQuickAnimatorController(q);
    delayedTouch = 0;

    QObject::connect(context, SIGNAL(initialized()), q, SIGNAL(sceneGraphInitialized()), Qt::DirectConnection);
    QObject::connect(context, SIGNAL(invalidated()), q, SIGNAL(sceneGraphInvalidated()), Qt::DirectConnection);
    QObject::connect(context, SIGNAL(invalidated()), q, SLOT(cleanupSceneGraph()), Qt::DirectConnection);

    QObject::connect(q, SIGNAL(focusObjectChanged(QObject*)), q, SIGNAL(activeFocusItemChanged()));
    QObject::connect(q, SIGNAL(screenChanged(QScreen*)), q, SLOT(forcePolish()));

    QObject::connect(q, SIGNAL(frameSwapped()), q, SLOT(runJobsAfterSwap()), Qt::DirectConnection);
/*!
    \property QQuickWindow::data
    \internal
*/

Alan Alpert's avatar
Alan Alpert committed
QQmlListProperty<QObject> QQuickWindowPrivate::data()
    return QQmlListProperty<QObject>(q_func(), 0, QQuickWindowPrivate::data_append,
                                             QQuickWindowPrivate::data_count,
                                             QQuickWindowPrivate::data_at,
                                             QQuickWindowPrivate::data_clear);
static QMouseEvent *touchToMouseEvent(QEvent::Type type, const QTouchEvent::TouchPoint &p, QTouchEvent *event, QQuickItem *item, bool transformNeeded = true)
    // The touch point local position and velocity are not yet transformed.
    QMouseEvent *me = new QMouseEvent(type, transformNeeded ? item->mapFromScene(p.scenePos()) : p.pos(), p.scenePos(), p.screenPos(),
                                      Qt::LeftButton, (type == QEvent::MouseButtonRelease ? Qt::NoButton : Qt::LeftButton), event->modifiers());
    me->setAccepted(true);
    me->setTimestamp(event->timestamp());
    QVector2D transformedVelocity = p.velocity();
    if (transformNeeded) {
        QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
Alan Alpert's avatar
Alan Alpert committed
        QMatrix4x4 transformMatrix(itemPrivate->windowToItemTransform());
        transformedVelocity = transformMatrix.mapVector(p.velocity()).toVector2D();
    }
    QGuiApplicationPrivate::setMouseEventCapsAndVelocity(me, event->device()->capabilities(), transformedVelocity);
    QGuiApplicationPrivate::setMouseEventSource(me, Qt::MouseEventSynthesizedByQt);
bool QQuickWindowPrivate::checkIfDoubleClicked(ulong newPressEventTimestamp)
{
    bool doubleClicked;

    if (touchMousePressTimestamp == 0) {
        // just initialize the variable
        touchMousePressTimestamp = newPressEventTimestamp;
        doubleClicked = false;
    } else {
        ulong timeBetweenPresses = newPressEventTimestamp - touchMousePressTimestamp;
        ulong doubleClickInterval = static_cast<ulong>(qApp->styleHints()->
                mouseDoubleClickInterval());
        doubleClicked = timeBetweenPresses < doubleClickInterval;
        if (doubleClicked) {
            touchMousePressTimestamp = 0;
        } else {
            touchMousePressTimestamp = newPressEventTimestamp;
        }
    }

    return doubleClicked;
}

Alan Alpert's avatar
Alan Alpert committed
bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *event)
    // For each point, check if it is accepted, if not, try the next point.
    // Any of the fingers can become the mouse one.
    // This can happen because a mouse area might not accept an event at some point but another.
    for (int i = 0; i < event->touchPoints().count(); ++i) {
        const QTouchEvent::TouchPoint &p = event->touchPoints().at(i);
        // A new touch point
        if (touchMouseId == -1 && p.state() & Qt::TouchPointPressed) {
            QPointF pos = item->mapFromScene(p.scenePos());

            // probably redundant, we check bounds in the calling function (matchingNewPoints)
            if (!item->contains(pos))
                break;

            // Store the id already here and restore it to -1 if the event does not get
            // accepted. Cannot defer setting the new value because otherwise if the event
            // handler spins the event loop all subsequent moves and releases get lost.
            touchMouseId = p.id();
            itemForTouchPointId[touchMouseId] = item;
            QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event, item, false));

            // Send a single press and see if that's accepted
            if (!mouseGrabberItem)
                item->grabMouse();
            item->grabTouchPoints(QVector<int>() << touchMouseId);

            QCoreApplication::sendEvent(item, mousePress.data());
            event->setAccepted(mousePress->isAccepted());
            if (!mousePress->isAccepted()) {
                if (itemForTouchPointId.value(p.id()) == item)
                    itemForTouchPointId.remove(p.id());

                if (mouseGrabberItem == item)
                    item->ungrabMouse();
            }

            if (mousePress->isAccepted() && checkIfDoubleClicked(event->timestamp())) {
                QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item, false));
                QCoreApplication::sendEvent(item, mouseDoubleClick.data());
                event->setAccepted(mouseDoubleClick->isAccepted());
                if (mouseDoubleClick->isAccepted()) {
                    touchMouseIdCandidates.clear();
            // The event was accepted, we are done.
            if (mousePress->isAccepted()) {
                touchMouseIdCandidates.clear();
            // The event was not accepted but touchMouseId was set.
                return false;
            // try the next point

        // Touch point was there before and moved
        } else if (p.id() == touchMouseId) {
            if (p.state() & Qt::TouchPointMoved) {
                    QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, mouseGrabberItem, false));
                    QCoreApplication::sendEvent(item, me.data());
                    event->setAccepted(me->isAccepted());
                    if (me->isAccepted()) {
                        itemForTouchPointId[p.id()] = mouseGrabberItem; // N.B. the mouseGrabberItem may be different after returning from sendEvent()
                        return true;
                    }
                } else {
                    // no grabber, check if we care about mouse hover
                    // FIXME: this should only happen once, not recursively... I'll ignore it just ignore hover now.
                    // hover for touch???
                    QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, item, false));
                    if (lastMousePosition.isNull())
                        lastMousePosition = me->windowPos();
                    QPointF last = lastMousePosition;
                    lastMousePosition = me->windowPos();
                    bool accepted = me->isAccepted();
                    bool delivered = deliverHoverEvent(contentItem, me->windowPos(), last, me->modifiers(), accepted);
                    if (!delivered) {
                        //take care of any exits
                        accepted = clearHover();
                    }
                    me->setAccepted(accepted);
                    break;
                }
            } else if (p.state() & Qt::TouchPointReleased) {
                // currently handled point was released
                    QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseButtonRelease, p, event, mouseGrabberItem, false));
                    QCoreApplication::sendEvent(item, me.data());
                    if (mouseGrabberItem) // might have ungrabbed due to event
                        mouseGrabberItem->ungrabMouse();
                    return me->isAccepted();
                }
void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber)
{
    Q_Q(QQuickWindow);
    if (mouseGrabberItem == grabber)
        return;

    QQuickItem *oldGrabber = mouseGrabberItem;
    mouseGrabberItem = grabber;

    if (touchMouseId != -1) {
        // update the touch item for mouse touch id to the new grabber
        itemForTouchPointId.remove(touchMouseId);
        if (grabber)
            itemForTouchPointId[touchMouseId] = grabber;
    }

    if (oldGrabber) {
        QEvent ev(QEvent::UngrabMouse);
        q->sendEvent(oldGrabber, &ev);
    }
}

Alan Alpert's avatar
Alan Alpert committed
void QQuickWindowPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform)
    QMatrix4x4 transformMatrix(transform);
    for (int i=0; i<touchPoints.count(); i++) {
        QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
        touchPoint.setRect(transform.mapRect(touchPoint.sceneRect()));
        touchPoint.setStartPos(transform.map(touchPoint.startScenePos()));
        touchPoint.setLastPos(transform.map(touchPoint.lastScenePos()));
        touchPoint.setVelocity(transformMatrix.mapVector(touchPoint.velocity()).toVector2D());
Alan Alpert's avatar
Alan Alpert committed
Translates the data in \a touchEvent to this window.  This method leaves the item local positions in
\a touchEvent untouched (these are filled in later).
*/
Alan Alpert's avatar
Alan Alpert committed
void QQuickWindowPrivate::translateTouchEvent(QTouchEvent *touchEvent)
{
    QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
    for (int i = 0; i < touchPoints.count(); ++i) {
        QTouchEvent::TouchPoint &touchPoint = touchPoints[i];

        touchPoint.setScreenRect(touchPoint.sceneRect());
        touchPoint.setStartScreenPos(touchPoint.startScenePos());
        touchPoint.setLastScreenPos(touchPoint.lastScenePos());

        touchPoint.setSceneRect(touchPoint.rect());
        touchPoint.setStartScenePos(touchPoint.startPos());
        touchPoint.setLastScenePos(touchPoint.lastPos());

            lastMousePosition = touchPoint.pos().toPoint();
    }
    touchEvent->setTouchPoints(touchPoints);
}


static inline bool windowHasFocus(QQuickWindow *win)
{
    const QWindow *focusWindow = QGuiApplication::focusWindow();
    return win == focusWindow || QQuickRenderControl::renderWindowFor(win) == focusWindow;
}

/*!
Set the focus inside \a scope to be \a item.
If the scope contains the active focus item, it will be changed to \a item.
Calls notifyFocusChangesRecur for all changed items.
*/
Caroline Chao's avatar
Caroline Chao committed
void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions options)
Alan Alpert's avatar
Alan Alpert committed
    Q_Q(QQuickWindow);

    Q_ASSERT(item);
    Q_ASSERT(scope || item == contentItem);
    qCDebug(DBG_FOCUS) << "QQuickWindowPrivate::setFocusInScope():";
    qCDebug(DBG_FOCUS) << "    scope:" << (QObject *)scope;
        qCDebug(DBG_FOCUS) << "    scopeSubFocusItem:" << (QObject *)QQuickItemPrivate::get(scope)->subFocusItem;
    qCDebug(DBG_FOCUS) << "    item:" << (QObject *)item;
    qCDebug(DBG_FOCUS) << "    activeFocusItem:" << (QObject *)activeFocusItem;
    QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0;
    QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
    QQuickItem *currentActiveFocusItem = activeFocusItem;
    QQuickItem *newActiveFocusItem = 0;
    QVarLengthArray<QQuickItem *, 20> changed;

    // Does this change the active focus?
    if (item == contentItem || scopePrivate->activeFocus) {
        QQuickItem *oldActiveFocusItem = 0;
        oldActiveFocusItem = activeFocusItem;
        if (item->isEnabled()) {
            newActiveFocusItem = item;
            while (newActiveFocusItem->isFocusScope()
                   && newActiveFocusItem->scopedFocusItem()
                   && newActiveFocusItem->scopedFocusItem()->isEnabled()) {
                newActiveFocusItem = newActiveFocusItem->scopedFocusItem();
            }
        } else {
            newActiveFocusItem = scope;
        if (oldActiveFocusItem) {
#ifndef QT_NO_IM
            qApp->inputMethod()->commit();
            activeFocusItem = 0;
Caroline Chao's avatar
Caroline Chao committed
            QFocusEvent event(QEvent::FocusOut, reason);
            q->sendEvent(oldActiveFocusItem, &event);

            QQuickItem *afi = oldActiveFocusItem;
            while (afi && afi != scope) {
                if (QQuickItemPrivate::get(afi)->activeFocus) {
                    QQuickItemPrivate::get(afi)->activeFocus = false;
                    changed << afi;
                }
                afi = afi->parentItem();
    if (item != contentItem && !(options & DontChangeSubFocusItem)) {
        QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
        if (oldSubFocusItem) {
            QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
            changed << oldSubFocusItem;
        }

        QQuickItemPrivate::get(item)->updateSubFocusItem(scope, true);
    }

    if (!(options & DontChangeFocusProperty)) {
        if (item != contentItem || windowHasFocus(q)) {
            itemPrivate->focus = true;
            changed << item;
    if (newActiveFocusItem && contentItem->hasFocus()) {
        activeFocusItem = newActiveFocusItem;

        QQuickItemPrivate::get(newActiveFocusItem)->activeFocus = true;
        changed << newActiveFocusItem;

        QQuickItem *afi = newActiveFocusItem->parentItem();
        while (afi && afi != scope) {
            if (afi->isFocusScope()) {
                QQuickItemPrivate::get(afi)->activeFocus = true;
                changed << afi;
            }
            afi = afi->parentItem();
        }
        updateFocusItemTransform();
Caroline Chao's avatar
Caroline Chao committed
        QFocusEvent event(QEvent::FocusIn, reason);
        q->sendEvent(newActiveFocusItem, &event);
    if (activeFocusItem != currentActiveFocusItem)
        emit q->focusObjectChanged(activeFocusItem);
    if (!changed.isEmpty())
        notifyFocusChangesRecur(changed.data(), changed.count() - 1);
}

Caroline Chao's avatar
Caroline Chao committed
void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions options)
Alan Alpert's avatar
Alan Alpert committed
    Q_Q(QQuickWindow);

    Q_ASSERT(item);
    Q_ASSERT(scope || item == contentItem);
    qCDebug(DBG_FOCUS) << "QQuickWindowPrivate::clearFocusInScope():";
    qCDebug(DBG_FOCUS) << "    scope:" << (QObject *)scope;
    qCDebug(DBG_FOCUS) << "    item:" << (QObject *)item;
    qCDebug(DBG_FOCUS) << "    activeFocusItem:" << (QObject *)activeFocusItem;
    QQuickItemPrivate *scopePrivate = 0;
    if (scope) {
        scopePrivate = QQuickItemPrivate::get(scope);
        if ( !scopePrivate->subFocusItem )
            return;//No focus, nothing to do.
    }
    QQuickItem *currentActiveFocusItem = activeFocusItem;
    QQuickItem *oldActiveFocusItem = 0;
    QQuickItem *newActiveFocusItem = 0;
    QVarLengthArray<QQuickItem *, 20> changed;
    Q_ASSERT(item == contentItem || item == scopePrivate->subFocusItem);

    // Does this change the active focus?
    if (item == contentItem || scopePrivate->activeFocus) {
        oldActiveFocusItem = activeFocusItem;
        newActiveFocusItem = scope;
#ifndef QT_NO_IM
        qApp->inputMethod()->commit();
#endif

        activeFocusItem = 0;

        if (oldActiveFocusItem) {
            QFocusEvent event(QEvent::FocusOut, reason);
            q->sendEvent(oldActiveFocusItem, &event);

            QQuickItem *afi = oldActiveFocusItem;
            while (afi && afi != scope) {
                if (QQuickItemPrivate::get(afi)->activeFocus) {
                    QQuickItemPrivate::get(afi)->activeFocus = false;
                    changed << afi;
                }
                afi = afi->parentItem();
    if (item != contentItem && !(options & DontChangeSubFocusItem)) {
        QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
        if (oldSubFocusItem && !(options & DontChangeFocusProperty)) {
            QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
            changed << oldSubFocusItem;
        }

        QQuickItemPrivate::get(item)->updateSubFocusItem(scope, false);

    } else if (!(options & DontChangeFocusProperty)) {
        QQuickItemPrivate::get(item)->focus = false;
        changed << item;
    }

    if (newActiveFocusItem) {
        Q_ASSERT(newActiveFocusItem == scope);
        activeFocusItem = scope;
        updateFocusItemTransform();
Caroline Chao's avatar
Caroline Chao committed
        QFocusEvent event(QEvent::FocusIn, reason);
        q->sendEvent(newActiveFocusItem, &event);
    if (activeFocusItem != currentActiveFocusItem)
        emit q->focusObjectChanged(activeFocusItem);
    if (!changed.isEmpty())
        notifyFocusChangesRecur(changed.data(), changed.count() - 1);
}

void QQuickWindowPrivate::clearFocusObject()
{
    if (activeFocusItem == contentItem)
        return;

    clearFocusInScope(contentItem, QQuickItemPrivate::get(contentItem)->subFocusItem, Qt::OtherFocusReason);
Alan Alpert's avatar
Alan Alpert committed
void QQuickWindowPrivate::notifyFocusChangesRecur(QQuickItem **items, int remaining)
    QPointer<QQuickItem> item(*items);

    if (remaining)
        notifyFocusChangesRecur(items + 1, remaining - 1);

    if (item) {
        QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);

        if (itemPrivate->notifiedFocus != itemPrivate->focus) {
            itemPrivate->notifiedFocus = itemPrivate->focus;
            emit item->focusChanged(itemPrivate->focus);
        }

        if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) {
            itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus;
            itemPrivate->itemChange(QQuickItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus);
            emit item->activeFocusChanged(itemPrivate->activeFocus);
        }
Alan Alpert's avatar
Alan Alpert committed
void QQuickWindowPrivate::dirtyItem(QQuickItem *)
Alan Alpert's avatar
Alan Alpert committed
    Q_Q(QQuickWindow);
    q->maybeUpdate();
}

Alan Alpert's avatar
Alan Alpert committed
void QQuickWindowPrivate::cleanup(QSGNode *n)
Alan Alpert's avatar
Alan Alpert committed
    Q_Q(QQuickWindow);

    Q_ASSERT(!cleanupNodeList.contains(n));
    cleanupNodeList.append(n);
    q->maybeUpdate();
}

    \qmltype Window
    \instantiates QQuickWindow
    \inqmlmodule QtQuick.Window
    \ingroup qtquick-visual
Nico Vertriest's avatar
Nico Vertriest committed
    \brief Creates a new top-level window
    The Window object creates a new top-level window for a Qt Quick scene. It automatically sets up the
    window for use with \c {QtQuick 2.x} graphical types.
Alan Alpert's avatar
Alan Alpert committed

    To use this type, you will need to import the module with the following line:
Alan Alpert's avatar
Alan Alpert committed
    \code
Kai Koehne's avatar
Kai Koehne committed
    import QtQuick.Window 2.2
Alan Alpert's avatar
Alan Alpert committed
    \endcode

    Omitting this import will allow you to have a QML environment without
    access to window system features.

    A Window can be declared inside an Item or inside another Window; in that
    case the inner Window will automatically become "transient for" the outer
    Window: that is, most platforms will show it centered upon the outer window
    by default, and there may be other platform-dependent behaviors, depending
    also on the \l flags. If the nested window is intended to be a dialog in
    your application, you should also set \l flags to Qt.Dialog, because some
    window managers will not provide the centering behavior without that flag.
    You can also declare multiple windows inside a top-level \l QtObject, in which
    case the windows will have no transient relationship.

    Alternatively you can set or bind \l x and \l y to position the Window
    explicitly on the screen.

    When the user attempts to close a window, the \a closing signal will be
    emitted. You can force the window to stay open (for example to prompt the
    user to save changes) by writing an onClosing handler and setting
    close.accepted = false.
Alan Alpert's avatar
Alan Alpert committed
    \class QQuickWindow
Alan Alpert's avatar
Alan Alpert committed
    \brief The QQuickWindow class provides the window for displaying a graphical QML scene
Alan Alpert's avatar
Alan Alpert committed
    QQuickWindow provides the graphical scene management needed to interact with and display
    a scene of QQuickItems.

Alan Alpert's avatar
Alan Alpert committed
    A QQuickWindow always has a single invisible root item. To add items to this window,
    reparent the items to the root item or to an existing item in the scene.

    For easily displaying a scene from a QML file, see \l{QQuickView}.
    QQuickWindow uses a scene graph on top of OpenGL to
    render. This scene graph is disconnected from the QML scene and
    potentially lives in another thread, depending on the platform
    implementation. Since the rendering scene graph lives
    independently from the QML scene, it can also be completely
    released without affecting the state of the QML scene.