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.
** 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.
**
** 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$
**
****************************************************************************/
#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>
#include <private/qquickanimatorcontroller_p.h>
#include <private/qguiapplication_p.h>
#include <QtGui/QInputMethod>
#include <private/qabstractanimation_p.h>
#include <QtGui/qevent.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>
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;
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));
}
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()));
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())
incubateAgain();
}
}
}
void animationStopped() { incubate(); }
protected:
virtual void incubatingObjectCountChanged(int count)
{
if (count && !m_renderLoop->interleaveIncubation())
incubateAgain();
QSGRenderLoop *m_renderLoop;
int m_incubation_time;
int m_timer;
#include "qquickwindow.moc"
#ifndef QT_NO_ACCESSIBILITY
/*!
Returns an accessibility interface for this window, or 0 if such an
interface cannot be created.
*/
QAccessibleInterface *QQuickWindow::accessibleRoot() const
return QAccessible::queryAccessibleInterface(const_cast<QQuickWindow*>(this));
/*
Focus behavior
==============
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.
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
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()
QQuickRootItem::QQuickRootItem()
if (d->windowManager)
d->windowManager->exposureChanged(this);
void QQuickWindow::resizeEvent(QResizeEvent *ev)
if (d->contentItem)
d->contentItem->setSize(ev->size());
if (d->windowManager)
d->windowManager->resize(this);
Q_D(QQuickWindow);
if (d->windowManager)
d->windowManager->show(this);
Q_D(QQuickWindow);
if (d->windowManager)
d->windowManager->hide(this);
d->updateFocusItemTransform();
int maxPolishCycles = 100000;
while (!itemsToPolish.isEmpty() && --maxPolishCycles > 0) {
QSet<QQuickItem *> itms = itemsToPolish;
for (QSet<QQuickItem *>::iterator it = itms.begin(); it != itms.end(); ++it) {
QQuickItem *item = *it;
QQuickItemPrivate::get(item)->polishScheduled = false;
item->updatePolish();
if (maxPolishCycles == 0)
qWarning("QQuickWindow: possible QQuickItem::polish() loop");
updateFocusItemTransform();
* Calling QQuickWindow::update() differs from QQuickItem::update() in that
* it always triggers a repaint, regardless of changes in the underlying
* scene graph or not.
*/
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);
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));
}
QML_MEMORY_SCOPE_STRING("SceneGraph");
animationController->beforeNodeSync();
emit q->beforeSynchronizing();
runAndClearJobs(&beforeSynchronizingJobs);
QSGRootNode *rootNode = new QSGRootNode;
rootNode->appendChildNode(QQuickItemPrivate::get(contentItem)->itemNode());
renderer = context->createRenderer();
renderer->setRootNode(rootNode);
}
animationController->afterNodeSync();
// Copy the current state of clearing from window into renderer.
renderer->setClearColor(clearColor);
QSGAbstractRenderer::ClearMode mode = QSGAbstractRenderer::ClearStencilBuffer | QSGAbstractRenderer::ClearDepthBuffer;
mode |= QSGAbstractRenderer::ClearColorBuffer;
renderer->setClearMode(mode);
renderer->setCustomRenderMode(customRenderMode);
emit q->afterSynchronizing();
runAndClearJobs(&afterSynchronizingJobs);
context->endSync();
void QQuickWindowPrivate::renderSceneGraph(const QSize &size)
QML_MEMORY_SCOPE_STRING("SceneGraph");
if (!renderer)
return;
animationController->advance();
emit q->beforeRendering();
runAndClearJobs(&beforeRenderingJobs);
int fboId = 0;
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);
runAndClearJobs(&afterRenderingJobs);
, activeFocusItem(0)
, mouseGrabberItem(0)
#ifndef QT_NO_CURSOR
, cursorItem(0)
#endif
#ifndef QT_NO_DRAGANDDROP
, dragGrabber(0)
, touchMouseId(-1)
, touchMousePressTimestamp(0)
, renderer(0)
, windowManager(0)
, clearColor(Qt::white)
, clearBeforeRendering(true)
, persistentGLContext(true)
, persistentSceneGraph(true)
, componentCompleted(true)
, lastFocusReason(Qt::OtherFocusReason)
, renderTarget(0)
, renderTargetId(0)
, vaoHelper(0)
#ifndef QT_NO_DRAGANDDROP
dragGrabber = new QQuickDragGrabber;
#endif
void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control)
contentItem = new QQuickRootItem;
QQmlEngine::setObjectOwnership(contentItem, QQmlEngine::CppOwnership);
QQuickItemPrivate *contentItemPrivate = QQuickItemPrivate::get(contentItem);
contentItemPrivate->window = q;
contentItemPrivate->windowRefCount = 1;
contentItemPrivate->flags |= QQuickItem::ItemIsFocusScope;
customRenderMode = qgetenv("QSG_VISUALIZE");
QQuickRenderControlPrivate::get(renderControl)->window = q;
if (!renderControl)
windowManager = QSGRenderLoop::instance();
Q_ASSERT(windowManager || renderControl);
QSGContext *sg;
if (renderControl) {
QQuickRenderControlPrivate *renderControlPriv = QQuickRenderControlPrivate::get(renderControl);
sg = renderControlPriv->sg;
context = renderControlPriv->rc;
windowManager->addWindow(q);
sg = windowManager->sceneGraphContext();
context = windowManager->createRenderContext(sg);
}
q->setSurfaceType(windowManager ? windowManager->windowSurfaceType() : QSurface::OpenGLSurface);
q->setFormat(sg->defaultSurfaceFormat());
animationController = new QQuickAnimatorController(q);
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
*/
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);
QMatrix4x4 transformMatrix(itemPrivate->windowToItemTransform());
transformedVelocity = transformMatrix.mapVector(p.velocity()).toVector2D();
}
QGuiApplicationPrivate::setMouseEventCapsAndVelocity(me, event->device()->capabilities(), transformedVelocity);
QGuiApplicationPrivate::setMouseEventSource(me, Qt::MouseEventSynthesizedByQt);
return me;
}
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;
}
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()) {
touchMouseId = -1;
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()) {
return true;
touchMouseId = -1;
// The event was accepted, we are done.
if (mousePress->isAccepted()) {
touchMouseIdCandidates.clear();
return true;
// The event was not accepted but touchMouseId was set.
if (touchMouseId != -1)
return false;
// try the next point
// Touch point was there before and moved
} else if (p.id() == touchMouseId) {
if (p.state() & Qt::TouchPointMoved) {
if (mouseGrabberItem) {
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
touchMouseId = -1;
if (mouseGrabberItem) {
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();
}
}
break;
}
}
return false;
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);
}
}
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());
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).
*/
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.
*/
void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions options)
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;
lastFocusReason = reason;
QVarLengthArray<QQuickItem *, 20> changed;
if (item == contentItem || scopePrivate->activeFocus) {
QQuickItem *oldActiveFocusItem = 0;
if (item->isEnabled()) {
newActiveFocusItem = item;
while (newActiveFocusItem->isFocusScope()
&& newActiveFocusItem->scopedFocusItem()
&& newActiveFocusItem->scopedFocusItem()->isEnabled()) {
newActiveFocusItem = newActiveFocusItem->scopedFocusItem();
}
} else {
newActiveFocusItem = scope;
q->sendEvent(oldActiveFocusItem, &event);
QQuickItem *afi = oldActiveFocusItem;
if (QQuickItemPrivate::get(afi)->activeFocus) {
QQuickItemPrivate::get(afi)->activeFocus = false;
changed << afi;
}
afi = afi->parentItem();
if (item != contentItem && !(options & DontChangeSubFocusItem)) {
QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
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()) {
QQuickItemPrivate::get(newActiveFocusItem)->activeFocus = true;
QQuickItem *afi = newActiveFocusItem->parentItem();
QQuickItemPrivate::get(afi)->activeFocus = true;
changed << afi;
}
afi = afi->parentItem();
}
q->sendEvent(newActiveFocusItem, &event);
if (activeFocusItem != currentActiveFocusItem)
emit q->focusObjectChanged(activeFocusItem);
notifyFocusChangesRecur(changed.data(), changed.count() - 1);
}
void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions options)
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;
lastFocusReason = reason;
QVarLengthArray<QQuickItem *, 20> changed;
Q_ASSERT(item == contentItem || item == scopePrivate->subFocusItem);
if (item == contentItem || scopePrivate->activeFocus) {
oldActiveFocusItem = activeFocusItem;
newActiveFocusItem = scope;
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;
}
if (newActiveFocusItem) {
Q_ASSERT(newActiveFocusItem == scope);
activeFocusItem = scope;
q->sendEvent(newActiveFocusItem, &event);
if (activeFocusItem != currentActiveFocusItem)
emit q->focusObjectChanged(activeFocusItem);
notifyFocusChangesRecur(changed.data(), changed.count() - 1);
}
void QQuickWindowPrivate::clearFocusObject()
{
if (activeFocusItem == contentItem)
return;
clearFocusInScope(contentItem, QQuickItemPrivate::get(contentItem)->subFocusItem, Qt::OtherFocusReason);
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);
}
Q_ASSERT(!cleanupNodeList.contains(n));
cleanupNodeList.append(n);
q->maybeUpdate();
}
\qmltype Window
\instantiates QQuickWindow
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.
To use this type, you will need to import the module with the following line:
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.
\brief The QQuickWindow class provides the window for displaying a graphical QML scene
QQuickWindow provides the graphical scene management needed to interact with and display
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.