diff options
author | Kent Hansen <kent.hansen@nokia.com> | 2012-03-23 14:31:47 +0100 |
---|---|---|
committer | Kent Hansen <kent.hansen@nokia.com> | 2012-03-23 14:31:47 +0100 |
commit | 0655209fdad022bd0f6eb20ce85522cb56506bf0 (patch) | |
tree | cdba0c1590655f5cb75a68cedff74f8a683db3a2 /src | |
parent | c3babc03c99c6ca5fa210486e133eb456a405bab (diff) | |
parent | 3d8f103c2641f35e7681485102a1b59886db8934 (diff) |
Merge master into api_changes
Conflicts:
src/qml/qml/qqmlboundsignal.cpp
src/qml/qml/qqmlpropertycache.cpp
Change-Id: I5193a193fa301c0b518291645bf626a5fa07118f
Diffstat (limited to 'src')
115 files changed, 1957 insertions, 1063 deletions
diff --git a/src/3rdparty/javascriptcore/DateMath.cpp b/src/3rdparty/javascriptcore/DateMath.cpp index 9f66d91013..be99d2ca25 100644 --- a/src/3rdparty/javascriptcore/DateMath.cpp +++ b/src/3rdparty/javascriptcore/DateMath.cpp @@ -345,8 +345,15 @@ double timeClip(double t) return NaN; return t >= 0 ? floor(t) : ceil(t); #else + +#if defined(__QNXNTO__) + if (!isfinite(t) || fabs(t) > maxECMAScriptTime) + return NaN; +#else if (!std::isfinite(t) || fabs(t) > maxECMAScriptTime) return NaN; +#endif + return trunc(t); #endif } diff --git a/src/imports/folderlistmodel/folderlistmodel.pro b/src/imports/folderlistmodel/folderlistmodel.pro index 592d8375c8..f30c2b2097 100644 --- a/src/imports/folderlistmodel/folderlistmodel.pro +++ b/src/imports/folderlistmodel/folderlistmodel.pro @@ -2,7 +2,7 @@ TARGET = qmlfolderlistmodelplugin TARGETPATH = Qt/labs/folderlistmodel include(../qimportbase.pri) -QT += qml +QT += core-private qml qml-private v8-private SOURCES += qquickfolderlistmodel.cpp plugin.cpp \ fileinfothread.cpp diff --git a/src/imports/folderlistmodel/qquickfolderlistmodel.cpp b/src/imports/folderlistmodel/qquickfolderlistmodel.cpp index 294fe184d2..0aabb7ae74 100644 --- a/src/imports/folderlistmodel/qquickfolderlistmodel.cpp +++ b/src/imports/folderlistmodel/qquickfolderlistmodel.cpp @@ -43,8 +43,8 @@ #include "qquickfolderlistmodel.h" #include "fileinfothread_p.h" #include "fileproperty_p.h" -#include <QDebug> #include <qqmlcontext.h> +#include <private/qqmlengine_p.h> QT_BEGIN_NAMESPACE @@ -365,7 +365,8 @@ void QQuickFolderListModel::setFolder(const QUrl &folder) if (folder == d->currentDir) return; - QString resolvedPath = QDir::cleanPath(folder.path()); + QString localPath = QQmlEnginePrivate::urlToLocalFileOrQrc(folder); + QString resolvedPath = QDir::cleanPath(QUrl(localPath).path()); beginResetModel(); @@ -407,7 +408,8 @@ void QQuickFolderListModel::setRootFolder(const QUrl &path) if (path.isEmpty()) return; - QString resolvedPath = QDir::cleanPath(path.path()); + QString localPath = QQmlEnginePrivate::urlToLocalFileOrQrc(path); + QString resolvedPath = QDir::cleanPath(QUrl(localPath).path()); QFileInfo info(resolvedPath); if (!info.exists() || !info.isDir()) diff --git a/src/plugins/accessible/shared/qqmlaccessible.cpp b/src/plugins/accessible/shared/qqmlaccessible.cpp index 70c6b90efe..54d0c06171 100644 --- a/src/plugins/accessible/shared/qqmlaccessible.cpp +++ b/src/plugins/accessible/shared/qqmlaccessible.cpp @@ -158,9 +158,9 @@ void QQmlAccessible::doAction(const QString &actionName) { // Look for and call the accessible[actionName]Action() function on the item. // This allows for overriding the default action handling. - const QByteArray functionName = "accessible" + actionName.toLatin1() + "Action()"; - if (object()->metaObject()->indexOfMethod(functionName) != -1) { - QMetaObject::invokeMethod(object(), functionName, Q_ARG(QString, actionName)); + const QByteArray functionName = "accessible" + actionName.toLatin1() + "Action"; + if (object()->metaObject()->indexOfMethod(functionName + "()") != -1) { + QMetaObject::invokeMethod(object(), functionName); return; } diff --git a/src/plugins/qmltooling/qmldbg_ost/qmlostplugin.cpp b/src/plugins/qmltooling/qmldbg_ost/qmlostplugin.cpp index 7d120cacb0..4dfed6c673 100644 --- a/src/plugins/qmltooling/qmldbg_ost/qmlostplugin.cpp +++ b/src/plugins/qmltooling/qmldbg_ost/qmlostplugin.cpp @@ -115,10 +115,11 @@ bool QmlOstPlugin::waitForMessage() return d->protocol->waitForReadyRead(-1); } -void QmlOstPlugin::setPort(int port, bool block) +void QmlOstPlugin::setPort(int port, bool block, const QString &hostaddress) { Q_UNUSED(port); Q_UNUSED(block); + Q_UNUSED(hostaddress); Q_D(QmlOstPlugin); diff --git a/src/plugins/qmltooling/qmldbg_ost/qmlostplugin.h b/src/plugins/qmltooling/qmldbg_ost/qmlostplugin.h index 41c3a07f25..257962570c 100644 --- a/src/plugins/qmltooling/qmldbg_ost/qmlostplugin.h +++ b/src/plugins/qmltooling/qmldbg_ost/qmlostplugin.h @@ -63,7 +63,7 @@ public: ~QmlOstPlugin(); void setServer(QQmlDebugServer *server); - void setPort(int port, bool bock); + void setPort(int port, bool bock, const QString &hostaddress); bool isConnected() const; void send(const QByteArray &message); diff --git a/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro b/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro index 0c5e196b18..fdd1f6e05f 100644 --- a/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro +++ b/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro @@ -9,10 +9,14 @@ DESTDIR = $$QT.qml.plugins/qmltooling QTDIR_build:REQUIRES += "contains(QT_CONFIG, qml)" SOURCES += \ - qtcpserverconnection.cpp + qtcpserverconnection.cpp \ + ../shared/qpacketprotocol.cpp HEADERS += \ - qtcpserverconnection.h + qtcpserverconnection.h \ + ../shared/qpacketprotocol.h + +INCLUDEPATH += ../shared OTHER_FILES += qtcpserverconnection.json diff --git a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp index 4eccd0dd55..724155a8f5 100644 --- a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp +++ b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp @@ -40,13 +40,13 @@ ****************************************************************************/ #include "qtcpserverconnection.h" +#include "qpacketprotocol.h" #include <QtCore/qplugin.h> #include <QtNetwork/qtcpserver.h> #include <QtNetwork/qtcpsocket.h> #include <private/qqmldebugserver_p.h> -#include <private/qpacketprotocol_p.h> QT_BEGIN_NAMESPACE @@ -56,6 +56,7 @@ public: int port; bool block; + QString hostaddress; QTcpSocket *socket; QPacketProtocol *protocol; QTcpServer *tcpServer; @@ -129,11 +130,12 @@ bool QTcpServerConnection::waitForMessage() return d->protocol->waitForReadyRead(-1); } -void QTcpServerConnection::setPort(int port, bool block) +void QTcpServerConnection::setPort(int port, bool block, const QString &hostaddress) { Q_D(QTcpServerConnection); d->port = port; d->block = block; + d->hostaddress = hostaddress; listen(); if (block) @@ -146,7 +148,17 @@ void QTcpServerConnection::listen() d->tcpServer = new QTcpServer(this); QObject::connect(d->tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection())); - if (d->tcpServer->listen(QHostAddress::Any, d->port)) + QHostAddress hostaddress; + if (!d->hostaddress.isEmpty()) { + if (!hostaddress.setAddress(d->hostaddress)) { + hostaddress = QHostAddress::Any; + qDebug("QML Debugger: Incorrect host address provided. So accepting connections " + "from any host."); + } + } else { + hostaddress = QHostAddress::Any; + } + if (d->tcpServer->listen(hostaddress, d->port)) qDebug("QML Debugger: Waiting for connection on port %d...", d->port); else qWarning("QML Debugger: Unable to listen to port %d.", d->port); @@ -194,4 +206,3 @@ void QTcpServerConnection::invalidPacket() } QT_END_NAMESPACE - diff --git a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.h b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.h index e81c9f7f0f..e2b32b0e4a 100644 --- a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.h +++ b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.h @@ -62,7 +62,7 @@ public: ~QTcpServerConnection(); void setServer(QQmlDebugServer *server); - void setPort(int port, bool bock); + void setPort(int port, bool bock, const QString &hostaddress); bool isConnected() const; void send(const QList<QByteArray> &messages); diff --git a/src/qml/debugger/qpacketprotocol.cpp b/src/plugins/qmltooling/shared/qpacketprotocol.cpp index 978054a238..a6a4e0e886 100644 --- a/src/qml/debugger/qpacketprotocol.cpp +++ b/src/plugins/qmltooling/shared/qpacketprotocol.cpp @@ -39,7 +39,7 @@ ** ****************************************************************************/ -#include "qpacketprotocol_p.h" +#include "qpacketprotocol.h" #include <QtCore/QBuffer> #include <QtCore/QElapsedTimer> diff --git a/src/qml/debugger/qpacketprotocol_p.h b/src/plugins/qmltooling/shared/qpacketprotocol.h index c6123d2836..0470c74643 100644 --- a/src/qml/debugger/qpacketprotocol_p.h +++ b/src/plugins/qmltooling/shared/qpacketprotocol.h @@ -42,34 +42,18 @@ #ifndef QPACKETPROTOCOL_H #define QPACKETPROTOCOL_H -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - #include <QtCore/qobject.h> #include <QtCore/qdatastream.h> -#include <private/qtqmlglobal_p.h> - -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE - class QIODevice; class QBuffer; class QPacket; class QPacketAutoSend; class QPacketProtocolPrivate; -class Q_QML_PRIVATE_EXPORT QPacketProtocol : public QObject +class QPacketProtocol : public QObject { Q_OBJECT public: @@ -101,7 +85,7 @@ private: }; -class Q_QML_PRIVATE_EXPORT QPacket : public QDataStream +class QPacket : public QDataStream { public: QPacket(); @@ -119,7 +103,7 @@ protected: QBuffer *buf; }; -class Q_QML_PRIVATE_EXPORT QPacketAutoSend : public QPacket +class QPacketAutoSend : public QPacket { public: virtual ~QPacketAutoSend(); @@ -132,6 +116,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif diff --git a/src/qml/debugger/debugger.pri b/src/qml/debugger/debugger.pri index f5abd2c196..f16e225cfd 100644 --- a/src/qml/debugger/debugger.pri +++ b/src/qml/debugger/debugger.pri @@ -1,5 +1,4 @@ SOURCES += \ - $$PWD/qpacketprotocol.cpp \ $$PWD/qqmldebugservice.cpp \ $$PWD/qqmlprofilerservice.cpp \ $$PWD/qqmldebugserver.cpp \ @@ -10,7 +9,6 @@ SOURCES += \ $$PWD/qdebugmessageservice.cpp HEADERS += \ - $$PWD/qpacketprotocol_p.h \ $$PWD/qqmldebugservice_p.h \ $$PWD/qqmldebugservice_p_p.h \ $$PWD/qqmlprofilerservice_p.h \ diff --git a/src/qml/debugger/qqmldebugserver.cpp b/src/qml/debugger/qqmldebugserver.cpp index ec3f9dafc2..dcf93b400e 100644 --- a/src/qml/debugger/qqmldebugserver.cpp +++ b/src/qml/debugger/qqmldebugserver.cpp @@ -117,9 +117,10 @@ public: m_pluginName = pluginName; } - void setPort(int port, bool block) { + void setPort(int port, bool block, QString &hostAddress) { m_port = port; m_block = block; + m_hostAddress = hostAddress; } void run(); @@ -128,6 +129,7 @@ private: QString m_pluginName; int m_port; bool m_block; + QString m_hostAddress; }; QQmlDebugServerPrivate::QQmlDebugServerPrivate() : @@ -214,7 +216,7 @@ void QQmlDebugServerThread::run() = server->d_func()->loadConnectionPlugin(m_pluginName); if (connection) { connection->setServer(QQmlDebugServer::instance()); - connection->setPort(m_port, m_block); + connection->setPort(m_port, m_block, m_hostAddress); } else { QCoreApplicationPrivate *appD = static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(qApp)); qWarning() << QString(QLatin1String("QML Debugger: Ignoring \"-qmljsdebugger=%1\". " @@ -258,8 +260,9 @@ QQmlDebugServer *QQmlDebugServer::instance() int port = 0; bool block = false; bool ok = false; + QString hostAddress; - // format: qmljsdebugger=port:3768[,block] OR qmljsdebugger=ost[,block] + // format: qmljsdebugger=port:3768[,host:<ip address>][,block] OR qmljsdebugger=ost[,block] if (!appD->qmljsDebugArgumentsString().isEmpty()) { if (!QQmlEnginePrivate::qml_debugging_enabled) { qWarning() << QString(QLatin1String( @@ -270,24 +273,30 @@ QQmlDebugServer *QQmlDebugServer::instance() } QString pluginName; - if (appD->qmljsDebugArgumentsString().indexOf(QLatin1String("port:")) == 0) { - int separatorIndex = appD->qmljsDebugArgumentsString().indexOf(QLatin1Char(',')); - port = appD->qmljsDebugArgumentsString().mid(5, separatorIndex - 5).toInt(&ok); - pluginName = QStringLiteral("qmldbg_tcp"); - } else if (appD->qmljsDebugArgumentsString().contains(QLatin1String("ost"))) { - pluginName = QStringLiteral("qmldbg_ost"); - ok = true; + QStringList lstjsDebugArguments = appD->qmljsDebugArgumentsString() + .split(QLatin1Char(',')); + foreach (const QString &strArgument, lstjsDebugArguments) { + if (strArgument.startsWith(QLatin1String("port:"))) { + port = strArgument.mid(5).toInt(&ok); + pluginName = QLatin1String("qmldbg_tcp"); + } else if (strArgument.startsWith(QLatin1String("host:"))) { + hostAddress = strArgument.mid(5); + } else if (strArgument == QLatin1String("block")) { + block = true; + } else { + qWarning() << QString::fromLatin1("QML Debugger: Invalid argument '%1' " + "detected. Ignoring the same.") + .arg(strArgument); + } } - block = appD->qmljsDebugArgumentsString().contains(QLatin1String("block")); - if (ok) { qQmlDebugServer = new QQmlDebugServer(); QQmlDebugServerThread *thread = new QQmlDebugServerThread; qQmlDebugServer->d_func()->thread = thread; qQmlDebugServer->moveToThread(thread); thread->setPluginName(pluginName); - thread->setPort(port, block); + thread->setPort(port, block, hostAddress); thread->start(); if (block) { diff --git a/src/qml/debugger/qqmldebugserverconnection_p.h b/src/qml/debugger/qqmldebugserverconnection_p.h index ab9e7bd73f..920e82ed47 100644 --- a/src/qml/debugger/qqmldebugserverconnection_p.h +++ b/src/qml/debugger/qqmldebugserverconnection_p.h @@ -69,7 +69,7 @@ public: virtual ~QQmlDebugServerConnection() {} virtual void setServer(QQmlDebugServer *server) = 0; - virtual void setPort(int port, bool bock) = 0; + virtual void setPort(int port, bool bock, const QString &hostaddress) = 0; virtual bool isConnected() const = 0; virtual void send(const QList<QByteArray> &messages) = 0; virtual void disconnect() = 0; diff --git a/src/qml/debugger/qqmlenginedebugservice.cpp b/src/qml/debugger/qqmlenginedebugservice.cpp index 67bec3577b..7f0bf7ca33 100644 --- a/src/qml/debugger/qqmlenginedebugservice.cpp +++ b/src/qml/debugger/qqmlenginedebugservice.cpp @@ -67,7 +67,7 @@ QQmlEngineDebugService *QQmlEngineDebugService::instance() } QQmlEngineDebugService::QQmlEngineDebugService(QObject *parent) - : QQmlDebugService(QStringLiteral("QDeclarativeEngine"), 1, parent), + : QQmlDebugService(QStringLiteral("QmlDebugger"), 1, parent), m_watch(new QQmlWatcher(this)), m_statesDelegate(0) { @@ -394,12 +394,10 @@ void QQmlEngineDebugService::processMessage(const QByteArray &message) QDataStream ds(message); QByteArray type; - ds >> type; + int queryId; + ds >> type >> queryId; if (type == "LIST_ENGINES") { - int queryId; - ds >> queryId; - QByteArray reply; QDataStream rs(&reply, QIODevice::WriteOnly); rs << QByteArray("LIST_ENGINES_R"); @@ -416,9 +414,8 @@ void QQmlEngineDebugService::processMessage(const QByteArray &message) sendMessage(reply); } else if (type == "LIST_OBJECTS") { - int queryId; int engineId = -1; - ds >> queryId >> engineId; + ds >> engineId; QQmlEngine *engine = qobject_cast<QQmlEngine *>(QQmlDebugService::objectForId(engineId)); @@ -443,12 +440,11 @@ void QQmlEngineDebugService::processMessage(const QByteArray &message) sendMessage(reply); } else if (type == "FETCH_OBJECT") { - int queryId; int objectId; bool recurse; bool dumpProperties = true; - ds >> queryId >> objectId >> recurse >> dumpProperties; + ds >> objectId >> recurse >> dumpProperties; QObject *object = QQmlDebugService::objectForId(objectId); @@ -464,10 +460,9 @@ void QQmlEngineDebugService::processMessage(const QByteArray &message) sendMessage(reply); } else if (type == "WATCH_OBJECT") { - int queryId; int objectId; - ds >> queryId >> objectId; + ds >> objectId; bool ok = m_watch->addWatch(queryId, objectId); QByteArray reply; @@ -476,11 +471,10 @@ void QQmlEngineDebugService::processMessage(const QByteArray &message) sendMessage(reply); } else if (type == "WATCH_PROPERTY") { - int queryId; int objectId; QByteArray property; - ds >> queryId >> objectId >> property; + ds >> objectId >> property; bool ok = m_watch->addWatch(queryId, objectId, property); QByteArray reply; @@ -489,11 +483,10 @@ void QQmlEngineDebugService::processMessage(const QByteArray &message) sendMessage(reply); } else if (type == "WATCH_EXPR_OBJECT") { - int queryId; int debugId; QString expr; - ds >> queryId >> debugId >> expr; + ds >> debugId >> expr; bool ok = m_watch->addWatch(queryId, debugId, expr); QByteArray reply; @@ -501,16 +494,17 @@ void QQmlEngineDebugService::processMessage(const QByteArray &message) rs << QByteArray("WATCH_EXPR_OBJECT_R") << queryId << ok; sendMessage(reply); } else if (type == "NO_WATCH") { - int queryId; + bool ok = m_watch->removeWatch(queryId); - ds >> queryId; - m_watch->removeWatch(queryId); + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + rs << QByteArray("NO_WATCH_R") << queryId << ok; + sendMessage(reply); } else if (type == "EVAL_EXPRESSION") { - int queryId; int objectId; QString expr; - ds >> queryId >> objectId >> expr; + ds >> objectId >> expr; QObject *object = QQmlDebugService::objectForId(objectId); QQmlContext *context = qmlContext(object); @@ -539,26 +533,43 @@ void QQmlEngineDebugService::processMessage(const QByteArray &message) bool isLiteralValue; QString filename; int line; - ds >> objectId >> propertyName >> expr >> isLiteralValue; - if (!ds.atEnd()) { // backward compatibility from 2.1, 2.2 - ds >> filename >> line; - } - setBinding(objectId, propertyName, expr, isLiteralValue, filename, line); + ds >> objectId >> propertyName >> expr >> isLiteralValue >> + filename >> line; + bool ok = setBinding(objectId, propertyName, expr, isLiteralValue, + filename, line); + + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + rs << QByteArray("SET_BINDING_R") << queryId << ok; + + sendMessage(reply); } else if (type == "RESET_BINDING") { int objectId; QString propertyName; ds >> objectId >> propertyName; - resetBinding(objectId, propertyName); + bool ok = resetBinding(objectId, propertyName); + + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + rs << QByteArray("RESET_BINDING_R") << queryId << ok; + + sendMessage(reply); } else if (type == "SET_METHOD_BODY") { int objectId; QString methodName; QString methodBody; ds >> objectId >> methodName >> methodBody; - setMethodBody(objectId, methodName, methodBody); + bool ok = setMethodBody(objectId, methodName, methodBody); + + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + rs << QByteArray("SET_METHOD_BODY_R") << queryId << ok; + + sendMessage(reply); } } -void QQmlEngineDebugService::setBinding(int objectId, +bool QQmlEngineDebugService::setBinding(int objectId, const QString &propertyName, const QVariant &expression, bool isLiteralValue, @@ -566,6 +577,7 @@ void QQmlEngineDebugService::setBinding(int objectId, int line, int column) { + bool ok = true; QObject *object = objectForId(objectId); QQmlContext *context = qmlContext(object); @@ -594,22 +606,23 @@ void QQmlEngineDebugService::setBinding(int objectId, oldBinding->destroy(); binding->update(); } else { + ok = false; qWarning() << "QQmlEngineDebugService::setBinding: unable to set property" << propertyName << "on object" << object; } } } else { // not a valid property - bool ok = false; if (m_statesDelegate) ok = m_statesDelegate->setBindingForInvalidProperty(object, propertyName, expression, isLiteralValue); if (!ok) qWarning() << "QQmlEngineDebugService::setBinding: unable to set property" << propertyName << "on object" << object; } } + return ok; } -void QQmlEngineDebugService::resetBinding(int objectId, const QString &propertyName) +bool QQmlEngineDebugService::resetBinding(int objectId, const QString &propertyName) { QObject *object = objectForId(objectId); QQmlContext *context = qmlContext(object); @@ -651,24 +664,25 @@ void QQmlEngineDebugService::resetBinding(int objectId, const QString &propertyN m_statesDelegate->resetBindingForInvalidProperty(object, propertyName); } } + return true; } -void QQmlEngineDebugService::setMethodBody(int objectId, const QString &method, const QString &body) +bool QQmlEngineDebugService::setMethodBody(int objectId, const QString &method, const QString &body) { QObject *object = objectForId(objectId); QQmlContext *context = qmlContext(object); if (!object || !context || !context->engine()) - return; + return false; QQmlContextData *contextData = QQmlContextData::get(context); if (!contextData) - return; + return false; QQmlPropertyData dummy; QQmlPropertyData *prop = QQmlPropertyCache::property(context->engine(), object, method, dummy); if (!prop || !prop->isVMEFunction()) - return; + return false; QMetaMethod metaMethod = object->metaObject()->method(prop->coreIndex); QList<QByteArray> paramNames = metaMethod.parameterNames(); @@ -690,6 +704,7 @@ void QQmlEngineDebugService::setMethodBody(int objectId, const QString &method, int lineNumber = vmeMetaObject->vmeMethodLineNumber(prop->coreIndex); vmeMetaObject->setVmeMethod(prop->coreIndex, QQmlExpressionPrivate::evalFunction(contextData, object, jsfunction, contextData->url.toString(), lineNumber)); + return true; } void QQmlEngineDebugService::propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value) @@ -729,7 +744,8 @@ void QQmlEngineDebugService::objectCreated(QQmlEngine *engine, QObject *object) QByteArray reply; QDataStream rs(&reply, QIODevice::WriteOnly); - rs << QByteArray("OBJECT_CREATED") << engineId << objectId; + //unique queryId -1 + rs << QByteArray("OBJECT_CREATED") << -1 << engineId << objectId; sendMessage(reply); } diff --git a/src/qml/debugger/qqmlenginedebugservice_p.h b/src/qml/debugger/qqmlenginedebugservice_p.h index f41063d7a3..19a5776e27 100644 --- a/src/qml/debugger/qqmlenginedebugservice_p.h +++ b/src/qml/debugger/qqmlenginedebugservice_p.h @@ -119,9 +119,9 @@ private: QQmlObjectData objectData(QObject *); QQmlObjectProperty propertyData(QObject *, int); QVariant valueContents(const QVariant &defaultValue) const; - void setBinding(int objectId, const QString &propertyName, const QVariant &expression, bool isLiteralValue, QString filename = QString(), int line = -1, int column = 0); - void resetBinding(int objectId, const QString &propertyName); - void setMethodBody(int objectId, const QString &method, const QString &body); + bool setBinding(int objectId, const QString &propertyName, const QVariant &expression, bool isLiteralValue, QString filename = QString(), int line = -1, int column = 0); + bool resetBinding(int objectId, const QString &propertyName); + bool setMethodBody(int objectId, const QString &method, const QString &body); QList<QQmlEngine *> m_engines; QQmlWatcher *m_watch; diff --git a/src/qml/debugger/qqmlprofilerservice_p.h b/src/qml/debugger/qqmlprofilerservice_p.h index 7a708456ba..2b4cafb615 100644 --- a/src/qml/debugger/qqmlprofilerservice_p.h +++ b/src/qml/debugger/qqmlprofilerservice_p.h @@ -54,12 +54,15 @@ // #include <private/qqmldebugservice_p.h> -#include <QtQml/qtqmlglobal.h> +#include "qqmlexpression.h" + #include <QtCore/qelapsedtimer.h> +#include <QtCore/qmetaobject.h> #include <QtCore/qmutex.h> #include <QtCore/qvector.h> #include <QtCore/qstringbuilder.h> + QT_BEGIN_HEADER QT_BEGIN_NAMESPACE @@ -194,47 +197,30 @@ struct QQmlBindingProfiler { QQmlProfilerService::instance->endRange(QQmlProfilerService::Binding); } - void addDetail(const QString &details) - { - if (enabled) - QQmlProfilerService::instance->rangeData(QQmlProfilerService::Binding, - details); - } -\ bool enabled; }; struct QQmlHandlingSignalProfiler { - QQmlHandlingSignalProfiler() + QQmlHandlingSignalProfiler(const QMetaMethod &signal, QQmlExpression *expression) { enabled = QQmlProfilerService::instance ? QQmlProfilerService::instance->profilingEnabled() : false; if (enabled) { - QQmlProfilerService::instance->startRange( - QQmlProfilerService::HandlingSignal); + QQmlProfilerService *service = QQmlProfilerService::instance; + service->startRange(QQmlProfilerService::HandlingSignal); + service->rangeData(QQmlProfilerService::HandlingSignal, + QString::fromLatin1(signal.methodSignature()) + QLatin1String(": ") + + expression->expression()); + service->rangeLocation(QQmlProfilerService::HandlingSignal, + expression->sourceFile(), expression->lineNumber(), + expression->columnNumber()); } } - void setSignalInfo(const QString &name, const QString &expression) - { - if (enabled) - QQmlProfilerService::instance->rangeData( - QQmlProfilerService::HandlingSignal, - name % QLatin1String(": ") % expression); - } - - void setLocation(const QString &file, int line, int column) - { - if (enabled) - QQmlProfilerService::instance->rangeLocation( - QQmlProfilerService::HandlingSignal, file, line, column); - } - ~QQmlHandlingSignalProfiler() { if (enabled) - QQmlProfilerService::instance->endRange( - QQmlProfilerService::HandlingSignal); + QQmlProfilerService::instance->endRange(QQmlProfilerService::HandlingSignal); } bool enabled; @@ -243,22 +229,23 @@ struct QQmlHandlingSignalProfiler { struct QQmlObjectCreatingProfiler { QQmlObjectCreatingProfiler() { - QQmlProfilerService *instance = QQmlProfilerService::instance; - enabled = instance ? - instance->profilingEnabled() : false; - if (enabled) - instance->startRange(QQmlProfilerService::Creating); + enabled = QQmlProfilerService::instance + ? QQmlProfilerService::instance->profilingEnabled() : false; + if (enabled) { + QQmlProfilerService *service = QQmlProfilerService::instance; + service->startRange(QQmlProfilerService::Creating); + } } void setTypeName(const QString &typeName) { - if (enabled) - QQmlProfilerService::instance->rangeData( - QQmlProfilerService::Creating, typeName); + Q_ASSERT_X(enabled, Q_FUNC_INFO, "method called although profiler is not enabled."); + QQmlProfilerService::instance->rangeData(QQmlProfilerService::Creating, typeName); } void setLocation(const QUrl &url, int line, int column) { + Q_ASSERT_X(enabled, Q_FUNC_INFO, "method called although profiler is not enabled."); if (enabled) QQmlProfilerService::instance->rangeLocation( QQmlProfilerService::Creating, url, line, column); diff --git a/src/qml/debugger/qv8debugservice.cpp b/src/qml/debugger/qv8debugservice.cpp index a6aeda31d5..7d54a59ac0 100644 --- a/src/qml/debugger/qv8debugservice.cpp +++ b/src/qml/debugger/qv8debugservice.cpp @@ -42,6 +42,7 @@ #include "qv8debugservice_p.h" #include "qqmldebugservice_p_p.h" #include <private/qjsconverter_impl_p.h> +#include <private/qv4compiler_p.h> #include <private/qv8engine_p.h> #include <QtCore/QHash> @@ -192,6 +193,7 @@ void QV8DebugService::init() Q_D(QV8DebugService); v8::Debug::SetMessageHandler2(DebugMessageHandler); v8::Debug::SetDebugMessageDispatchHandler(DebugMessageDispatchHandler); + QV4Compiler::enableV4(false); d->initializeMutex.unlock(); } diff --git a/src/qml/debugger/qv8profilerservice.cpp b/src/qml/debugger/qv8profilerservice.cpp index 6208676522..c75c258785 100644 --- a/src/qml/debugger/qv8profilerservice.cpp +++ b/src/qml/debugger/qv8profilerservice.cpp @@ -52,20 +52,19 @@ Q_GLOBAL_STATIC(QV8ProfilerService, v8ProfilerInstance) class DebugServiceOutputStream : public v8::OutputStream { - QQmlDebugService &_service; public: - DebugServiceOutputStream(QQmlDebugService &service) - : v8::OutputStream(), - _service(service) {} + DebugServiceOutputStream() + : v8::OutputStream() {} void EndOfStream() {} WriteResult WriteAsciiChunk(char *rawData, int size) { QByteArray data; QDataStream ds(&data, QIODevice::WriteOnly); ds << QV8ProfilerService::V8SnapshotChunk << QByteArray(rawData, size); - _service.sendMessage(data); + messages.append(data); return kContinue; } + QList<QByteArray> messages; }; // convert to a QByteArray that can be sent to the debug client @@ -267,16 +266,18 @@ void QV8ProfilerServicePrivate::takeSnapshot(v8::HeapSnapshot::Type snapshotType v8::HandleScope scope; v8::Local<v8::String> title = v8::String::New(""); - DebugServiceOutputStream outputStream(*q); + DebugServiceOutputStream outputStream; const v8::HeapSnapshot *snapshot = v8::HeapProfiler::TakeSnapshot(title, snapshotType); snapshot->Serialize(&outputStream, v8::HeapSnapshot::kJSON); + QList<QByteArray> messages = outputStream.messages; //indicate completion QByteArray data; QDataStream ds(&data, QIODevice::WriteOnly); ds << (int)QV8ProfilerService::V8SnapshotComplete; + messages.append(data); - q->sendMessage(data); + q->sendMessages(messages); } void QV8ProfilerServicePrivate::sendMessages() @@ -285,16 +286,16 @@ void QV8ProfilerServicePrivate::sendMessages() QList<QByteArray> messages; for (int i = 0; i < m_data.count(); ++i) - messages << m_data.at(i).toByteArray(); - q->sendMessages(messages); + messages.append(m_data.at(i).toByteArray()); m_data.clear(); //indicate completion QByteArray data; QDataStream ds(&data, QIODevice::WriteOnly); ds << (int)QV8ProfilerService::V8Complete; + messages.append(data); - q->sendMessage(data); + q->sendMessages(messages); } diff --git a/src/qml/qml/ftw/qhashedstring_p.h b/src/qml/qml/ftw/qhashedstring_p.h index f575285ff6..f058f21e98 100644 --- a/src/qml/qml/ftw/qhashedstring_p.h +++ b/src/qml/qml/ftw/qhashedstring_p.h @@ -59,6 +59,10 @@ #include <private/qflagpointer_p.h> +#if defined(Q_OS_QNX) +#include <stdlib.h> +#endif + QT_BEGIN_NAMESPACE // Enable this to debug hash linking assumptions. diff --git a/src/qml/qml/ftw/qqmlpool.cpp b/src/qml/qml/ftw/qqmlpool.cpp index 6fd11d4b1e..64df87ada5 100644 --- a/src/qml/qml/ftw/qqmlpool.cpp +++ b/src/qml/qml/ftw/qqmlpool.cpp @@ -41,6 +41,10 @@ #include "qqmlpool_p.h" +#ifdef Q_OS_QNX +#include <malloc.h> +#endif + // #define POOL_DEBUG QT_BEGIN_NAMESPACE diff --git a/src/qml/qml/qqmlaccessors_p.h b/src/qml/qml/qqmlaccessors_p.h index a603bede9f..8e67a58511 100644 --- a/src/qml/qml/qqmlaccessors_p.h +++ b/src/qml/qml/qqmlaccessors_p.h @@ -47,6 +47,10 @@ #include <QtCore/qhash.h> #include <QtCore/QReadWriteLock> +#ifdef Q_OS_QNX +#include <stdint.h> +#endif + QT_BEGIN_HEADER QT_BEGIN_NAMESPACE diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index f43822ed0e..c4f48ef63d 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -193,8 +193,6 @@ void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags) if (!updatingFlag()) { QQmlBindingProfiler prof(m_url, m_lineNumber, m_columnNumber); - if (prof.enabled) - prof.addDetail(expression()); setUpdatingFlag(true); QQmlAbstractExpression::DeleteWatcher watcher(this); diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index aa130d9493..f7d0c00a5c 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -174,13 +174,7 @@ int QQmlBoundSignal::qt_metacall(QMetaObject::Call c, int id, void **a) if (QQmlDebugService::isDebuggingEnabled()) QV8DebugService::instance()->signalEmitted(QString::fromAscii(m_signal.methodSignature().constData())); - QQmlHandlingSignalProfiler prof; - if (prof.enabled) { - prof.setSignalInfo(QString::fromLatin1(m_signal.methodSignature().constData()), - m_expression->expression()); - prof.setLocation(m_expression->sourceFile(), m_expression->lineNumber(), - m_expression->columnNumber()); - } + QQmlHandlingSignalProfiler prof(m_signal, m_expression); m_isEvaluating = true; if (!m_paramsValid) { @@ -233,7 +227,10 @@ QQmlBoundSignalParameters::QQmlBoundSignalParameters(const QMetaMethod &method, prop.setWritable(false); } else { QByteArray propType = type; - if (t >= int(QVariant::UserType) || t == QMetaType::UnknownType || t == QMetaType::Void) { + if ((QMetaType::typeFlags(t) & QMetaType::IsEnumeration) == QMetaType::IsEnumeration) { + t = QVariant::Int; + propType = "int"; + } else if (t == QMetaType::UnknownType) { QByteArray scope; QByteArray name; int scopeIdx = propType.lastIndexOf("::"); diff --git a/src/qml/qml/qqmlcompiler.cpp b/src/qml/qml/qqmlcompiler.cpp index c3b8152ce1..dec9911481 100644 --- a/src/qml/qml/qqmlcompiler.cpp +++ b/src/qml/qml/qqmlcompiler.cpp @@ -2217,7 +2217,7 @@ bool QQmlCompiler::buildValueTypeProperty(QObject *type, //optimization for <Type>.<EnumValue> enum assignments bool isEnumAssignment = false; - if (prop->core.isEnum()) + if (prop->core.isEnum() || prop->core.propType == QMetaType::Int) COMPILE_CHECK(testQualifiedEnumAssignment(prop, obj, value, &isEnumAssignment)); if (isEnumAssignment) { @@ -2485,7 +2485,7 @@ bool QQmlCompiler::buildPropertyLiteralAssignment(QQmlScript::Property *prop, if (v->value.isScript()) { //optimization for <Type>.<EnumValue> enum assignments - if (prop->core.isEnum()) { + if (prop->core.isEnum() || prop->core.propType == QMetaType::Int) { bool isEnumAssignment = false; COMPILE_CHECK(testQualifiedEnumAssignment(prop, obj, v, &isEnumAssignment)); if (isEnumAssignment) { @@ -2515,8 +2515,9 @@ bool QQmlCompiler::testQualifiedEnumAssignment(QQmlScript::Property *prop, QQmlScript::Value *v, bool *isAssignment) { + bool isIntProp = (prop->core.propType == QMetaType::Int) && !prop->core.isEnum(); *isAssignment = false; - if (!prop->core.isEnum()) + if (!prop->core.isEnum() && !isIntProp) return true; QMetaProperty mprop = obj->metaObject()->property(prop->index); @@ -2528,6 +2529,17 @@ bool QQmlCompiler::testQualifiedEnumAssignment(QQmlScript::Property *prop, if (!string.at(0).isUpper()) return true; + if (isIntProp) { + // Allow enum assignment to ints. + int enumval = evaluateEnum(string.toUtf8()); + if (enumval != -1) { + v->type = Value::Literal; + v->value = QQmlScript::Variant((double)enumval); + *isAssignment = true; + } + return true; + } + QStringList parts = string.split(QLatin1Char('.')); if (parts.count() != 2) return true; diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 6cd5cf6cec..416178e3e1 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -829,7 +829,10 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context) if (rv) { QQmlData *ddata = QQmlData::get(rv); Q_ASSERT(ddata); + //top level objects should never get JS ownership. + //if JS ownership is needed this needs to be explicitly undone (like in component.createObject()) ddata->indestructible = true; + ddata->explicitIndestructibleSet = true; } if (enginePriv->isDebugging && rv) { @@ -1120,7 +1123,8 @@ void QQmlComponent::createObject(QQmlV8Function *args) d->completeCreate(); Q_ASSERT(QQmlData::get(rv)); - QQmlData::get(rv)->setImplicitDestructible(); + QQmlData::get(rv)->explicitIndestructibleSet = false; + QQmlData::get(rv)->indestructible = false; if (!rv) args->returnValue(v8::Null()); @@ -1255,10 +1259,6 @@ void QQmlComponentPrivate::initializeObjectWithInitialProperties(v8::Handle<v8:: v8::Handle<v8::Value> args[] = { object, valuemap }; v8::Handle<v8::Function>::Cast(function)->Call(v8engine->global(), 2, args); } - - QQmlData *ddata = QQmlData::get(toCreate); - Q_ASSERT(ddata); - ddata->setImplicitDestructible(); } diff --git a/src/qml/qml/qqmlcontext_p.h b/src/qml/qml/qqmlcontext_p.h index d10543bde5..97bc04b91d 100644 --- a/src/qml/qml/qqmlcontext_p.h +++ b/src/qml/qml/qqmlcontext_p.h @@ -146,7 +146,7 @@ public: quint32 isJSContext:1; quint32 isPragmaLibraryContext:1; quint32 unresolvedNames:1; // True if expressions in this context failed to resolve a toplevel name - quint32 dummy:28; + quint32 dummy:27; QQmlContext *publicContext; // VME data that is constructing this context if any diff --git a/src/qml/qml/qqmldirparser.cpp b/src/qml/qml/qqmldirparser.cpp index 7b99214f04..df5f7f8ee9 100644 --- a/src/qml/qml/qqmldirparser.cpp +++ b/src/qml/qml/qqmldirparser.cpp @@ -295,4 +295,18 @@ QList<QQmlDirParser::TypeInfo> QQmlDirParser::typeInfos() const } #endif +QDebug &operator<< (QDebug &debug, const QQmlDirParser::Component &component) +{ + return debug << qPrintable(QString("{%1 %2.%3}").arg(component.typeName) + .arg(component.majorVersion) + .arg(component.minorVersion)); +} + +QDebug &operator<< (QDebug &debug, const QQmlDirParser::Script &script) +{ + return debug << qPrintable(QString("{%1 %2.%3}").arg(script.nameSpace) + .arg(script.majorVersion) + .arg(script.minorVersion)); +} + QT_END_NAMESPACE diff --git a/src/qml/qml/qqmldirparser_p.h b/src/qml/qml/qqmldirparser_p.h index 8c681309ac..f46e1781da 100644 --- a/src/qml/qml/qqmldirparser_p.h +++ b/src/qml/qml/qqmldirparser_p.h @@ -55,6 +55,7 @@ #include <QtCore/QUrl> #include <QtCore/QHash> +#include <QtCore/QDebug> QT_BEGIN_NAMESPACE @@ -160,6 +161,8 @@ private: typedef QList<QQmlDirParser::Component> QQmlDirComponents; typedef QList<QQmlDirParser::Script> QQmlDirScripts; +QDebug &operator<< (QDebug &, const QQmlDirParser::Component &); +QDebug &operator<< (QDebug &, const QQmlDirParser::Script &); QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlextensionplugin.cpp b/src/qml/qml/qqmlextensionplugin.cpp index b69fa5da7a..86d9f9588d 100644 --- a/src/qml/qml/qqmlextensionplugin.cpp +++ b/src/qml/qml/qqmlextensionplugin.cpp @@ -97,7 +97,7 @@ QT_BEGIN_NAMESPACE \code TEMPLATE = lib CONFIG += qt plugin - QT += declarative + QT += qml DESTDIR = com/nokia/TimeExample TARGET = qmlqtimeexampleplugin diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 0c1fb3bd93..1224efdaac 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -117,7 +117,7 @@ public: QList<QQmlError> *errors); QString resolvedUri(const QString &dir_arg, QQmlImportDatabase *database); - bool add(const QQmlDirComponents &qmldircomponentsnetwork, + QString add(const QQmlDirComponents &qmldircomponentsnetwork, const QString& uri_arg, const QString& prefix, int vmaj, int vmin, QQmlScript::Import::Type importType, QQmlImportDatabase *database, QList<QQmlError> *errors); @@ -493,7 +493,7 @@ QString QQmlImportsPrivate::resolvedUri(const QString &dir_arg, QQmlImportDataba return stableRelativePath; } -bool QQmlImportsPrivate::add(const QQmlDirComponents &qmldircomponentsnetwork, +QString QQmlImportsPrivate::add(const QQmlDirComponents &qmldircomponentsnetwork, const QString& uri_arg, const QString& prefix, int vmaj, int vmin, QQmlScript::Import::Type importType, QQmlImportDatabase *database, QList<QQmlError> *errors) @@ -540,7 +540,7 @@ bool QQmlImportsPrivate::add(const QQmlDirComponents &qmldircomponentsnetwork, url = QUrl::fromLocalFile(fi.absolutePath()).toString(); uri = resolvedUri(dir, database); if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, &qmldirscripts, errors)) - return false; + return QString(); break; } } @@ -564,7 +564,7 @@ bool QQmlImportsPrivate::add(const QQmlDirComponents &qmldircomponentsnetwork, url = QUrl::fromLocalFile(fi.absolutePath()).toString(); uri = resolvedUri(dir, database); if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, &qmldirscripts, errors)) - return false; + return QString(); break; } } @@ -586,7 +586,7 @@ bool QQmlImportsPrivate::add(const QQmlDirComponents &qmldircomponentsnetwork, url = QUrl::fromLocalFile(absolutePath).toString(); uri = resolvedUri(dir, database); if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, &qmldirscripts, errors)) - return false; + return QString(); break; } } @@ -604,7 +604,7 @@ bool QQmlImportsPrivate::add(const QQmlDirComponents &qmldircomponentsnetwork, error.setDescription(QQmlImportDatabase::tr("module \"%1\" is not installed").arg(uri_arg)); errors->prepend(error); } - return false; + return QString(); } } else { if (importType == QQmlScript::Import::File && qmldircomponents.isEmpty()) { @@ -619,14 +619,14 @@ bool QQmlImportsPrivate::add(const QQmlDirComponents &qmldircomponentsnetwork, error.setUrl(QUrl(importUrl)); errors->prepend(error); } - return false; // local import dirs must exist + return QString(); // local import dirs must exist } uri = resolvedUri(dir, database); if (uri.endsWith(Slash)) uri.chop(1); if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) { if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,&qmldirscripts,errors)) - return false; + return QString(); } } else { if (prefix.isEmpty()) { @@ -642,7 +642,7 @@ bool QQmlImportsPrivate::add(const QQmlDirComponents &qmldircomponentsnetwork, error.setUrl(QUrl(importUrl)); errors->prepend(error); } - return false; + return QString(); } } } @@ -669,7 +669,7 @@ bool QQmlImportsPrivate::add(const QQmlDirComponents &qmldircomponentsnetwork, error.setDescription(QQmlImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin)); errors->prepend(error); } - return false; + return QString(); } } @@ -686,7 +686,7 @@ bool QQmlImportsPrivate::add(const QQmlDirComponents &qmldircomponentsnetwork, QQmlError error; error.setDescription(QQmlImportDatabase::tr("\"%1\" is ambiguous. Found in %2 and in %3").arg(uri).arg(url).arg(it->url)); errors->prepend(error); - return false; + return QString(); } } @@ -716,7 +716,7 @@ bool QQmlImportsPrivate::add(const QQmlDirComponents &qmldircomponentsnetwork, s->imports.prepend(data); - return true; + return data.url; } bool QQmlImportsPrivate::find(const QString& type, int *vmajor, int *vminor, QQmlType** type_return, @@ -874,9 +874,11 @@ QQmlImportDatabase::~QQmlImportDatabase() The \a prefix may be empty, in which case the import location is considered for unqualified types. + Returns the resolved URL of the import on success. + The base URL must already have been set with Import::setBaseUrl(). */ -bool QQmlImports::addImport(QQmlImportDatabase *importDb, +QString QQmlImports::addImport(QQmlImportDatabase *importDb, const QString& uri, const QString& prefix, int vmaj, int vmin, QQmlScript::Import::Type importType, const QQmlDirComponents &qmldircomponentsnetwork, diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h index ff19510525..422f2429a0 100644 --- a/src/qml/qml/qqmlimport_p.h +++ b/src/qml/qml/qqmlimport_p.h @@ -93,7 +93,7 @@ public: QQmlType** type_return, QString* url_return, int *version_major, int *version_minor) const; - bool addImport(QQmlImportDatabase *, + QString addImport(QQmlImportDatabase *, const QString& uri, const QString& prefix, int vmaj, int vmin, QQmlScript::Import::Type importType, const QQmlDirComponents &qmldircomponentsnetwork, diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp index aa9777d89e..fad2ae2f28 100644 --- a/src/qml/qml/qqmlincubator.cpp +++ b/src/qml/qml/qqmlincubator.cpp @@ -293,8 +293,9 @@ void QQmlIncubatorPrivate::incubate(QQmlVME::Interrupt &i) if (result) { QQmlData *ddata = QQmlData::get(result); Q_ASSERT(ddata); + //see QQmlComponent::beginCreate for explanation of indestructible ddata->indestructible = true; - + ddata->explicitIndestructibleSet = true; q->setInitialState(result); } diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 5b80f57d01..4af08c28bf 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -512,7 +512,7 @@ QObject *QQmlType::create() const d->m_newFunc(rv); if (rv && !d->m_metaObjects.isEmpty()) - (void *)new QQmlProxyMetaObject(rv, &d->m_metaObjects); + (void)new QQmlProxyMetaObject(rv, &d->m_metaObjects); return rv; } @@ -525,7 +525,7 @@ void QQmlType::create(QObject **out, void **memory, size_t additionalMemory) con d->m_newFunc(rv); if (rv && !d->m_metaObjects.isEmpty()) - (void *)new QQmlProxyMetaObject(rv, &d->m_metaObjects); + (void)new QQmlProxyMetaObject(rv, &d->m_metaObjects); *out = rv; *memory = ((char *)rv) + d->m_allocationSize; diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index afab665ea4..4a1eb79213 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -674,7 +674,13 @@ QStringList QQmlPropertyCache::propertyNames() const return keys; } -static int EnumType(const QMetaObject *meta, const QByteArray &str) +struct StaticQtMetaObject : public QObject +{ + static const QMetaObject *get() + { return &static_cast<StaticQtMetaObject*> (0)->staticQtMetaObject; } +}; + +static int EnumType(const QMetaObject *metaobj, const QByteArray &str) { QByteArray scope; QByteArray name; @@ -685,6 +691,11 @@ static int EnumType(const QMetaObject *meta, const QByteArray &str) } else { name = str; } + const QMetaObject *meta; + if (scope == "Qt") + meta = StaticQtMetaObject::get(); + else + meta = metaobj; for (int i = meta->enumeratorCount() - 1; i >= 0; --i) { QMetaEnum m = meta->enumerator(i); if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope))) @@ -727,12 +738,14 @@ int *QQmlPropertyCache::methodParameterTypes(QObject *object, int index, for (int ii = 0; ii < argc; ++ii) { int type = m.parameterType(ii); - if (type == QVariant::Invalid) { + if ((QMetaType::typeFlags(type) & QMetaType::IsEnumeration) == QMetaType::IsEnumeration) + type = QVariant::Int; + else if (type == QMetaType::UnknownType) { if (argTypeNames.isEmpty()) argTypeNames = m.parameterTypes(); type = EnumType(object->metaObject(), argTypeNames.at(ii)); } - if (type == QVariant::Invalid) { + if (type == QMetaType::UnknownType) { if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii); free(args); return 0; @@ -754,12 +767,14 @@ int *QQmlPropertyCache::methodParameterTypes(QObject *object, int index, for (int ii = 0; ii < argc; ++ii) { int type = m.parameterType(ii); - if (type == QVariant::Invalid) { + if ((QMetaType::typeFlags(type) & QMetaType::IsEnumeration) == QMetaType::IsEnumeration) + type = QVariant::Int; + else if (type == QMetaType::UnknownType) { if (argTypeNames.isEmpty()) argTypeNames = m.parameterTypes(); type = EnumType(object->metaObject(), argTypeNames.at(ii)); } - if (type == QVariant::Invalid) { + if (type == QMetaType::UnknownType) { if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii); return 0; } diff --git a/src/qml/qml/qqmlstringconverters.cpp b/src/qml/qml/qqmlstringconverters.cpp index 473097fab0..04b63ea7cd 100644 --- a/src/qml/qml/qqmlstringconverters.cpp +++ b/src/qml/qml/qqmlstringconverters.cpp @@ -128,6 +128,9 @@ QDateTime QQmlStringConverters::dateTimeFromString(const QString &s, bool *ok) { QDateTime d = QDateTime::fromString(s, Qt::ISODate); if (ok) *ok = d.isValid(); + // V8 never parses a date string as local time. For consistency do the same here. + if (d.timeSpec() == Qt::LocalTime) + d.setTimeSpec(Qt::UTC); return d; } #endif // QT_NO_DATESTRING diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 781915e23e..abe2c0bfa4 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1558,7 +1558,6 @@ void QQmlTypeData::dataReceived(const QByteArray &data) ref.qualifier = import.qualifier; ref.script = blob; m_scripts << ref; - } } @@ -1657,8 +1656,8 @@ void QQmlTypeData::resolveTypes() import.extractVersion(&vmaj, &vmin); QList<QQmlError> errors; - if (!m_imports.addImport(importDatabase, import.uri, import.qualifier, - vmaj, vmin, import.type, qmldircomponentsnetwork, &errors)) { + if (m_imports.addImport(importDatabase, import.uri, import.qualifier, vmaj, vmin, + import.type, qmldircomponentsnetwork, &errors).isNull()) { QQmlError error; if (errors.size()) { error = errors.takeFirst(); @@ -1859,8 +1858,9 @@ void QQmlScriptBlob::dataReceived(const QByteArray &data) import.extractVersion(&vmaj, &vmin); QList<QQmlError> errors; - if (!m_imports.addImport(importDatabase, import.uri, import.qualifier, vmaj, vmin, - import.type, QQmlDirComponents(), &errors)) { + QString importUrl = m_imports.addImport(importDatabase, import.uri, import.qualifier, vmaj, vmin, + import.type, QQmlDirComponents(), &errors); + if (importUrl.isNull()) { QQmlError error = errors.takeFirst(); // description should be set by addImport(). error.setUrl(m_imports.baseUrl()); @@ -1871,6 +1871,22 @@ void QQmlScriptBlob::dataReceived(const QByteArray &data) setError(errors); return; } + + // Does this library contain any scripts? + QUrl libraryUrl(importUrl); + const QQmlDirParser *dirParser = typeLoader()->qmlDirParser(libraryUrl.path() + QLatin1String("qmldir")); + foreach (const QQmlDirParser::Script &script, dirParser->scripts()) { + QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName)); + QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl); + addDependency(blob); + + ScriptReference ref; + ref.location = import.location.start; + ref.qualifier = script.nameSpace; + ref.nameSpace = import.qualifier; + ref.script = blob; + m_scripts << ref; + } } } } @@ -1902,11 +1918,20 @@ void QQmlScriptBlob::done() m_scriptData->urlString = finalUrlString(); m_scriptData->importCache = new QQmlTypeNameCache(); - for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) { - const ScriptReference &script = m_scripts.at(ii); + QSet<QString> ns; + + for (int scriptIndex = 0; !isError() && scriptIndex < m_scripts.count(); ++scriptIndex) { + const ScriptReference &script = m_scripts.at(scriptIndex); m_scriptData->scripts.append(script.script); - m_scriptData->importCache->add(script.qualifier, ii); + + if (!script.nameSpace.isNull()) { + if (!ns.contains(script.nameSpace)) { + ns.insert(script.nameSpace); + m_scriptData->importCache->add(script.nameSpace); + } + } + m_scriptData->importCache->add(script.qualifier, scriptIndex, script.nameSpace); } m_imports.populateCache(m_scriptData->importCache, engine); diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index c8c2756bd5..319b2e7a10 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -390,6 +390,7 @@ public: QQmlScript::Location location; QString qualifier; + QString nameSpace; QQmlScriptBlob *script; }; diff --git a/src/qml/qml/qqmlvme.cpp b/src/qml/qml/qqmlvme.cpp index 6309335e59..352684ed5e 100644 --- a/src/qml/qml/qqmlvme.cpp +++ b/src/qml/qml/qqmlvme.cpp @@ -829,7 +829,7 @@ QObject *QQmlVME::run(QList<QQmlError> *errors, bindValues.push(binding); binding->m_mePtr = &bindValues.top(); - Q_ASSERT(binding->propertyIndex() == property); + Q_ASSERT(binding->propertyIndex() == (property & 0xFF00FFFF)); Q_ASSERT(binding->object() == target); binding->addToObject(); diff --git a/src/qml/qml/qqmlwatcher.cpp b/src/qml/qml/qqmlwatcher.cpp index 500185762f..4eaa3d2c2a 100644 --- a/src/qml/qml/qqmlwatcher.cpp +++ b/src/qml/qml/qqmlwatcher.cpp @@ -166,13 +166,14 @@ bool QQmlWatcher::addWatch(int id, quint32 objectId, const QString &expr) return false; } -void QQmlWatcher::removeWatch(int id) +bool QQmlWatcher::removeWatch(int id) { if (!m_proxies.contains(id)) - return; + return false; QList<QPointer<QQmlWatchProxy> > proxies = m_proxies.take(id); qDeleteAll(proxies); + return true; } void QQmlWatcher::addPropertyWatch(int id, QObject *object, quint32 debugId, const QMetaProperty &property) diff --git a/src/qml/qml/qqmlwatcher_p.h b/src/qml/qml/qqmlwatcher_p.h index 70dc9d468c..3ca2c83777 100644 --- a/src/qml/qml/qqmlwatcher_p.h +++ b/src/qml/qml/qqmlwatcher_p.h @@ -77,7 +77,7 @@ public: bool addWatch(int id, quint32 objectId, const QByteArray &property); bool addWatch(int id, quint32 objectId, const QString &expr); - void removeWatch(int id); + bool removeWatch(int id); Q_SIGNALS: void propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value); diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index df1712976c..56743eb915 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -1384,7 +1384,7 @@ void QQmlXMLHttpRequest::readEncoding() if (header.first == "content-type") { int separatorIdx = header.second.indexOf(';'); if (separatorIdx == -1) { - m_mime == header.second; + m_mime = header.second; } else { m_mime = header.second.mid(0, separatorIdx); int charsetIdx = header.second.indexOf("charset="); diff --git a/src/qml/qml/v4/qv4bindings.cpp b/src/qml/qml/v4/qv4bindings.cpp index 38a3acafa2..0cd6ffd082 100644 --- a/src/qml/qml/v4/qv4bindings.cpp +++ b/src/qml/qml/v4/qv4bindings.cpp @@ -1153,6 +1153,25 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, } QML_V4_END_INSTR(ConvertColorToString, unaryop) + QML_V4_BEGIN_INSTR(ConvertObjectToBool, unaryop) + { + const Register &src = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + // ### NaN + if (src.isUndefined()) + output.setUndefined(); + else + output.setbool(src.getQObject() != 0); + } + QML_V4_END_INSTR(ConvertObjectToBool, unaryop) + + QML_V4_BEGIN_INSTR(ConvertNullToObject, unaryop) + { + Register &output = registers[instr->unaryop.output]; + output.setQObject(0); + } + QML_V4_END_INSTR(ConvertNullToObject, unaryop) + QML_V4_BEGIN_INSTR(ResolveUrl, unaryop) { const Register &src = registers[instr->unaryop.src]; diff --git a/src/qml/qml/v4/qv4compiler.cpp b/src/qml/qml/v4/qv4compiler.cpp index 09b0f3861b..c9495e8987 100644 --- a/src/qml/qml/v4/qv4compiler.cpp +++ b/src/qml/qml/v4/qv4compiler.cpp @@ -54,7 +54,6 @@ QT_BEGIN_NAMESPACE DEFINE_BOOL_CONFIG_OPTION(bindingsDump, QML_BINDINGS_DUMP) DEFINE_BOOL_CONFIG_OPTION(qmlDisableOptimizer, QML_DISABLE_OPTIMIZER) -DEFINE_BOOL_CONFIG_OPTION(qmlExperimental, QML_EXPERIMENTAL) DEFINE_BOOL_CONFIG_OPTION(qmlVerboseCompiler, QML_VERBOSE_COMPILER) DEFINE_BOOL_CONFIG_OPTION(qmlBindingsTestEnv, QML_BINDINGS_TEST) @@ -958,6 +957,7 @@ void QV4CompilerPrivate::visitMove(IR::Move *s) case IR::StringType: opcode = V4Instr::ConvertStringToBool; break; case IR::UrlType: opcode = V4Instr::ConvertUrlToBool; break; case IR::ColorType: opcode = V4Instr::ConvertColorToBool; break; + case IR::ObjectType: opcode = V4Instr::ConvertObjectToBool; break; default: break; } // switch } else if (targetTy == IR::IntType) { @@ -1010,6 +1010,11 @@ void QV4CompilerPrivate::visitMove(IR::Move *s) case IR::StringType: opcode = V4Instr::ConvertStringToColor; break; default: break; } // switch + } else if (targetTy == IR::ObjectType) { + switch (sourceTy) { + case IR::NullType: opcode = V4Instr::ConvertNullToObject; break; + default: break; + } // switch } if (opcode != V4Instr::Noop) { V4Instr conv; @@ -1354,9 +1359,6 @@ int QV4Compiler::compile(const Expression &expression, QQmlEnginePrivate *engine { if (!expression.expression.asAST()) return false; - if (!qmlExperimental() && expression.property->isValueTypeSubProperty) - return -1; - if (qmlDisableOptimizer() || !qmlEnableV4) return -1; diff --git a/src/qml/qml/v4/qv4instruction.cpp b/src/qml/qml/v4/qv4instruction.cpp index 1ed8bd245e..cb6ff40589 100644 --- a/src/qml/qml/v4/qv4instruction.cpp +++ b/src/qml/qml/v4/qv4instruction.cpp @@ -189,6 +189,12 @@ void Bytecode::dump(const V4Instr *i, int address) const case V4Instr::ConvertColorToString: INSTR_DUMP << "\t" << "ConvertColorToString" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; + case V4Instr::ConvertObjectToBool: + INSTR_DUMP << "\t" << "ConvertObjectToBool" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; + case V4Instr::ConvertNullToObject: + INSTR_DUMP << "\t" << "ConvertNullToObject" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + break; case V4Instr::ResolveUrl: INSTR_DUMP << "\t" << "ResolveUrl" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; diff --git a/src/qml/qml/v4/qv4instruction_p.h b/src/qml/qml/v4/qv4instruction_p.h index d6c790e46f..239cb362cc 100644 --- a/src/qml/qml/v4/qv4instruction_p.h +++ b/src/qml/qml/v4/qv4instruction_p.h @@ -98,6 +98,8 @@ QT_BEGIN_NAMESPACE F(ConvertUrlToString, unaryop) \ F(ConvertColorToBool, unaryop) \ F(ConvertColorToString, unaryop) \ + F(ConvertObjectToBool, unaryop) \ + F(ConvertNullToObject, unaryop) \ F(ResolveUrl, unaryop) \ F(MathSinReal, unaryop) \ F(MathCosReal, unaryop) \ diff --git a/src/qml/qml/v4/qv4irbuilder.cpp b/src/qml/qml/v4/qv4irbuilder.cpp index 481b23eb4a..ea4d1afbbc 100644 --- a/src/qml/qml/v4/qv4irbuilder.cpp +++ b/src/qml/qml/v4/qv4irbuilder.cpp @@ -428,7 +428,7 @@ bool QV4IRBuilder::visit(AST::IdentifierExpression *ast) if (r.isValid()) { if (r.type) { _expr.code = _block->ATTACH_TYPE(name, r.type, IR::Name::ScopeStorage, line, column); - } else if (r.importNamespace) { + } /*else if (r.importNamespace) { QQmlMetaType::ModuleApiInstance *moduleApi = m_expression->importCache->moduleApi(r.importNamespace); if (moduleApi) { if (moduleApi->qobjectCallback) { @@ -439,7 +439,7 @@ bool QV4IRBuilder::visit(AST::IdentifierExpression *ast) if (moduleApi->qobjectApi) _expr.code = _block->MODULE_OBJECT(name, moduleApi->qobjectApi->metaObject(), IR::Name::MemberStorage, line, column); } - } + }*/ //### we can't create the actual QObject here, as we may be running in a thread (can be reenabled once QTBUG-24894 is handled) // We don't support anything else } else { bool found = false; diff --git a/src/qml/qml/v8/qjsconverter_impl_p.h b/src/qml/qml/v8/qjsconverter_impl_p.h index 10b8ab5fae..c2775df7f5 100644 --- a/src/qml/qml/v8/qjsconverter_impl_p.h +++ b/src/qml/qml/v8/qjsconverter_impl_p.h @@ -44,6 +44,10 @@ #ifndef QJSCONVERTER_IMPL_P_H #define QJSCONVERTER_IMPL_P_H +#ifdef Q_OS_QNX +#include <malloc.h> +#endif + QT_BEGIN_NAMESPACE extern char *qdtoa(double d, int mode, int ndigits, int *decpt, int *sign, char **rve, char **digits_str); diff --git a/src/qml/qml/v8/qjsvalue.cpp b/src/qml/qml/v8/qjsvalue.cpp index 4471e68a61..6ecb97d2e5 100644 --- a/src/qml/qml/v8/qjsvalue.cpp +++ b/src/qml/qml/v8/qjsvalue.cpp @@ -428,7 +428,7 @@ quint32 QJSValue::toUInt() const \table \header \li Input Type \li Result \row \li Undefined \li An invalid QVariant. - \row \li Null \li An invalid QVariant. + \row \li Null \li A QVariant containing a null pointer (QMetaType::VoidStar). \row \li Boolean \li A QVariant containing the value of the boolean. \row \li Number \li A QVariant containing the value of the number. \row \li String \li A QVariant containing the value of the string. diff --git a/src/qml/qml/v8/qjsvalue_impl_p.h b/src/qml/qml/v8/qjsvalue_impl_p.h index fbddcfa5ba..8d22204843 100644 --- a/src/qml/qml/v8/qjsvalue_impl_p.h +++ b/src/qml/qml/v8/qjsvalue_impl_p.h @@ -281,7 +281,7 @@ QVariant QJSValuePrivate::toVariant() const case CNumber: return QVariant(u.m_number); case CNull: - return QVariant(); + return QVariant(QMetaType::VoidStar, 0); case CUndefined: return QVariant(); case JSValue: diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index 0cd5d0ad6c..9c570d756b 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -1061,7 +1061,9 @@ v8::Handle<v8::Value> createQmlObject(const v8::Arguments &args) QObject *obj = component.beginCreate(effectiveContext); if (obj) { - QQmlData::get(obj, true)->setImplicitDestructible(); + QQmlData::get(obj, true)->explicitIndestructibleSet = false; + QQmlData::get(obj)->indestructible = false; + obj->setParent(parentArg); @@ -1084,7 +1086,7 @@ v8::Handle<v8::Value> createQmlObject(const v8::Arguments &args) } /*! -\qmlmethod object Qt::createComponent(url, mode) +\qmlmethod object Qt::createComponent(url, mode, parent) Returns a \l Component object created using the QML file at the specified \a url, or \c null if an empty string was given. @@ -1099,6 +1101,9 @@ will be \c Component.Loading while it is loading. The status will change to \c Component.Ready if the component loads successfully, or \c Component.Error if loading fails. +If the optional \a parent parameter is given, it should refer to the object +that will become the parent for the created \l Component object. + Call \l {Component::createObject()}{Component.createObject()} on the returned component to create an object instance of the component. @@ -1114,7 +1119,8 @@ use \l{QML:Qt::createQmlObject()}{Qt.createQmlObject()}. v8::Handle<v8::Value> createComponent(const v8::Arguments &args) { const char *invalidArgs = "Qt.createComponent(): Invalid arguments"; - if (args.Length() < 1 || args.Length() > 2) + const char *invalidParent = "Qt.createComponent(): Invalid parent object"; + if (args.Length() < 1 || args.Length() > 3) V8THROW_ERROR(invalidArgs); QV8Engine *v8engine = V8ENGINE(); @@ -1131,21 +1137,46 @@ v8::Handle<v8::Value> createComponent(const v8::Arguments &args) return v8::Null(); QQmlComponent::CompilationMode compileMode = QQmlComponent::PreferSynchronous; - if (args.Length() == 2) { + + // Default to engine parent; this will be removed in the near future (QTBUG-24841) + QObject *parentArg = engine; + + unsigned consumedCount = 1; + if (args.Length() > 1) { + const v8::Local<v8::Value> &lastArg = args[args.Length()-1]; + + // The second argument could be the mode enum if (args[1]->IsInt32()) { int mode = args[1]->Int32Value(); if (mode != int(QQmlComponent::PreferSynchronous) && mode != int(QQmlComponent::Asynchronous)) V8THROW_ERROR(invalidArgs); compileMode = QQmlComponent::CompilationMode(mode); + consumedCount += 1; } else { - V8THROW_ERROR(invalidArgs); + // The second argument could be the parent only if there are exactly two args + if ((args.Length() != 2) || !(lastArg->IsObject() || lastArg->IsNull())) + V8THROW_ERROR(invalidArgs); + } + + if (consumedCount < args.Length()) { + if (lastArg->IsObject()) { + parentArg = v8engine->toQObject(lastArg); + if (!parentArg) + V8THROW_ERROR(invalidParent); + } else if (lastArg->IsNull()) { + parentArg = 0; + } else { + V8THROW_ERROR(invalidParent); + } } } QUrl url = context->resolvedUrl(QUrl(arg)); - QQmlComponent *c = new QQmlComponent(engine, url, compileMode, engine); + QQmlComponent *c = new QQmlComponent(engine, url, compileMode, parentArg); QQmlComponentPrivate::get(c)->creationContext = effectiveContext; - QQmlData::get(c, true)->setImplicitDestructible(); + QQmlData::get(c, true)->explicitIndestructibleSet = false; + QQmlData::get(c)->indestructible = false; + return v8engine->newQObject(c); } diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp index f0df025541..678f9aa3ee 100644 --- a/src/qml/qml/v8/qv8engine.cpp +++ b/src/qml/qml/v8/qv8engine.cpp @@ -1368,7 +1368,8 @@ v8::Handle<v8::Value> QV8Engine::variantToJS(const QVariant &value) } // Converts a JS value to a QVariant. -// Null, Undefined -> QVariant() (invalid) +// Undefined -> QVariant() (invalid) +// Null -> QVariant((void*)0) // Boolean -> QVariant(bool) // Number -> QVariant(double) // String -> QVariant(QString) @@ -1379,8 +1380,10 @@ v8::Handle<v8::Value> QV8Engine::variantToJS(const QVariant &value) QVariant QV8Engine::variantFromJS(v8::Handle<v8::Value> value) { Q_ASSERT(!value.IsEmpty()); - if (value->IsNull() || value->IsUndefined()) + if (value->IsUndefined()) return QVariant(); + if (value->IsNull()) + return QVariant(QMetaType::VoidStar, 0); if (value->IsBoolean()) return value->ToBoolean()->Value(); if (value->IsInt32()) diff --git a/src/qmldevtools/qmldevtools.pro b/src/qmldevtools/qmldevtools.pro index 97b3788dcd..4dd21c6110 100644 --- a/src/qmldevtools/qmldevtools.pro +++ b/src/qmldevtools/qmldevtools.pro @@ -17,4 +17,4 @@ HEADERS += qtqmldevtoolsversion.h unix|win32-g++*:QMAKE_PKGCONFIG_REQUIRES = QtCore -include($$QT.qml.sources/qml/parser/parser.pri) +include(../qml/qml/parser/parser.pri) diff --git a/src/qmltest/qmltest.pro b/src/qmltest/qmltest.pro index b1c99d2739..6df36d20cb 100644 --- a/src/qmltest/qmltest.pro +++ b/src/qmltest/qmltest.pro @@ -4,7 +4,7 @@ TARGET = QtQuickTest QPRO_PWD = $$PWD CONFIG += module -CONFIG += dll warn_on declarative_debug +CONFIG += dll warn_on MODULE_PRI += ../../modules/qt_qmltest.pri QT += testlib testlib-private qml quick gui diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index f8e5e3c57f..a605b9ce6d 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -63,6 +63,10 @@ #include <private/qv8domerrors_p.h> #include <QtCore/qnumeric.h> +#ifdef Q_OS_QNX +#include <ctype.h> +#endif + QT_BEGIN_NAMESPACE /*! \qmlclass Context2D QQuickContext2D diff --git a/src/quick/items/qquickaccessibleattached.cpp b/src/quick/items/qquickaccessibleattached.cpp index 46f85417d2..27efebca0c 100644 --- a/src/quick/items/qquickaccessibleattached.cpp +++ b/src/quick/items/qquickaccessibleattached.cpp @@ -144,6 +144,10 @@ QQuickAccessibleAttached::QQuickAccessibleAttached(QObject *parent) // Enable accessibility for items with accessible content. This also // enables accessibility for the ancestors of souch items. item->d_func()->setAccessibleFlagAndListener(); +#ifndef QT_NO_ACCESSIBILITY + QAccessibleEvent ev(item, QAccessible::ObjectCreated); + QAccessible::updateAccessibility(&ev); +#endif } QQuickAccessibleAttached::~QQuickAccessibleAttached() diff --git a/src/quick/items/qquickaccessibleattached_p.h b/src/quick/items/qquickaccessibleattached_p.h index 129c3ef240..419f21a4b3 100644 --- a/src/quick/items/qquickaccessibleattached_p.h +++ b/src/quick/items/qquickaccessibleattached_p.h @@ -85,6 +85,10 @@ public: if (name != m_name) { m_name = name; emit nameChanged(); +#ifndef QT_NO_ACCESSIBILITY + QAccessibleEvent ev(parent(), QAccessible::NameChanged); + QAccessible::updateAccessibility(&ev); +#endif } } @@ -94,6 +98,10 @@ public: if (m_description != description) { m_description = description; emit descriptionChanged(); +#ifndef QT_NO_ACCESSIBILITY + QAccessibleEvent ev(parent(), QAccessible::DescriptionChanged); + QAccessible::updateAccessibility(&ev); +#endif } } diff --git a/src/quick/items/qquickcanvas.cpp b/src/quick/items/qquickcanvas.cpp index cd3bbbfa2a..a11f68f709 100644 --- a/src/quick/items/qquickcanvas.cpp +++ b/src/quick/items/qquickcanvas.cpp @@ -306,6 +306,8 @@ QQuickCanvasPrivate::QQuickCanvasPrivate() , windowManager(0) , clearColor(Qt::white) , clearBeforeRendering(true) + , persistentGLContext(false) + , persistentSceneGraph(false) , renderTarget(0) , renderTargetId(0) , incubationController(0) @@ -325,6 +327,7 @@ void QQuickCanvasPrivate::init(QQuickCanvas *c) rootItem = new QQuickRootItem; QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(rootItem); rootItemPrivate->canvas = q; + rootItemPrivate->canvasRefCount = 1; rootItemPrivate->flags |= QQuickItem::ItemIsFocusScope; // In the absence of a focus in event on some platforms assume the window will @@ -536,7 +539,7 @@ void QQuickCanvasPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, F q->sendEvent(oldActiveFocusItem, &event); QQuickItem *afi = oldActiveFocusItem; - while (afi != scope) { + while (afi && afi != scope) { if (QQuickItemPrivate::get(afi)->activeFocus) { QQuickItemPrivate::get(afi)->activeFocus = false; changed << afi; @@ -548,27 +551,12 @@ void QQuickCanvasPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, F if (item != rootItem && !(options & DontChangeSubFocusItem)) { QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem; - // Correct focus chain in scope - if (oldSubFocusItem) { - QQuickItem *sfi = scopePrivate->subFocusItem->parentItem(); - while (sfi != scope) { - QQuickItemPrivate::get(sfi)->subFocusItem = 0; - sfi = sfi->parentItem(); - } - } - { - scopePrivate->subFocusItem = item; - QQuickItem *sfi = scopePrivate->subFocusItem->parentItem(); - while (sfi != scope) { - QQuickItemPrivate::get(sfi)->subFocusItem = item; - sfi = sfi->parentItem(); - } - } - if (oldSubFocusItem) { QQuickItemPrivate::get(oldSubFocusItem)->focus = false; changed << oldSubFocusItem; } + + QQuickItemPrivate::get(item)->updateSubFocusItem(scope, true); } if (!(options & DontChangeFocusProperty)) { @@ -647,7 +635,7 @@ void QQuickCanvasPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, q->sendEvent(oldActiveFocusItem, &event); QQuickItem *afi = oldActiveFocusItem; - while (afi != scope) { + while (afi && afi != scope) { if (QQuickItemPrivate::get(afi)->activeFocus) { QQuickItemPrivate::get(afi)->activeFocus = false; changed << afi; @@ -658,20 +646,13 @@ void QQuickCanvasPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, if (item != rootItem && !(options & DontChangeSubFocusItem)) { QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem; - // Correct focus chain in scope - if (oldSubFocusItem) { - QQuickItem *sfi = scopePrivate->subFocusItem->parentItem(); - while (sfi != scope) { - QQuickItemPrivate::get(sfi)->subFocusItem = 0; - sfi = sfi->parentItem(); - } - } - scopePrivate->subFocusItem = 0; - 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; @@ -750,6 +731,55 @@ void QQuickCanvasPrivate::cleanup(QSGNode *n) 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}. + + + \section1 Scene Graph and Rendering + + The QQuickCanvas 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. + + The sceneGraphInitialized() signal is emitted on the rendering thread before the QML scene is + rendered to the screen for the first time. If the rendering scene graph has been released + the signal will be emitted again before the next frame is rendered. + + Rendering is done by first copying the QML scene's state into the rendering scene graph. This is + done by calling QQuickItem::updatePaintNode() functions on all items that have changed. This phase + is run on the rendering thread with the GUI thread blocked, when a separate rendering thread + is being used. The scene can then be rendered. + + Before the scene graph is rendered, the beforeRendering() signal is emitted. The OpenGL context + is bound at this point and the application is free to do its own rendering. Also + make sure to disable the clearing of the color buffer, using setClearBeforeRendering(). The + default clear color is white and can be changed with setClearColor(). After the scene has + been rendered, the afterRendering() signal is emitted. The application can use this to render + OpenGL on top of a QML application. Once the frame is fully done and has been swapped, + the frameSwapped() signal is emitted. + + While the scene graph is being rendered on the rendering thread, the GUI will process animations + for the next frame. This means that as long as users are not using scene graph API + directly, the added complexity of a rendering thread can be completely ignored. + + When a QQuickCanvas is programatically hidden with hide() or setVisible(false), it will + stop rendering and its scene graph and OpenGL context might be released. The + sceneGraphInvalidated() signal will be emitted when this happens. + + \warning It is crucial that OpenGL operations and interaction with the scene graph happens + exclusively on the rendering thread, primarily during the updatePaintNode() phase. + + \warning As signals related to rendering might be emitted from the rendering thread, + connections should be made using Qt::DirectConnection + + + \section1 Resource Management + + QML will typically try to cache images, scene graph nodes, etc to improve performance, but in + some low-memory scenarios it might be required to aggressively release these resources. The + releaseResources() can be used to force clean up of certain resources. Calling releaseResources() + may result in the entire scene graph and its OpenGL context being deleted. The + sceneGraphInvalidated() signal will be emitted when this happens. + */ QQuickCanvas::QQuickCanvas(QWindow *parent) : QWindow(*(new QQuickCanvasPrivate), parent) @@ -771,11 +801,6 @@ QQuickCanvas::~QQuickCanvas() d->windowManager->canvasDestroyed(this); - // ### should we change ~QQuickItem to handle this better? - // manually cleanup for the root item (item destructor only handles these when an item is parented) - QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(d->rootItem); - rootItemPrivate->removeFromDirtyList(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); delete d->incubationController; d->incubationController = 0; @@ -786,6 +811,15 @@ QQuickCanvas::~QQuickCanvas() /*! This function tries to release redundant resources currently held by the QML scene. + + Calling this function might result in the scene graph and the OpenGL context used + for rendering being released to release graphics memory. If this happens, the + sceneGraphInvalidated() signal will be called, allowing users to clean up their + own graphics resources. The setPersistentOpenGLContext() and setPersistentSceneGraph() + functions can be used to prevent this from happening, if handling the cleanup is + not feasible in the application, at the cost of higher memory usage. + + \sa sceneGraphInvalidated(), setPersistentOpenGLContext(), setPersistentSceneGraph(). */ void QQuickCanvas::releaseResources() @@ -798,6 +832,69 @@ void QQuickCanvas::releaseResources() /*! + Controls whether the OpenGL context can be released as a part of a call to + releaseResources(). + + The OpenGL context might still be released when the user makes an explicit + call to hide(). + + \sa setPersistentSceneGraph() + */ + +void QQuickCanvas::setPersistentOpenGLContext(bool persistent) +{ + Q_D(QQuickCanvas); + d->persistentGLContext = persistent; +} + + +/*! + Returns whether the OpenGL context can be released as a part of a call to + releaseResources(). + */ + +bool QQuickCanvas::isPersistentOpenGLContext() const +{ + Q_D(const QQuickCanvas); + return d->persistentGLContext; +} + + + +/*! + Controls whether the scene graph nodes and resources can be released as a + part of a call to releaseResources(). + + The scene graph nodes and resources might still be released when the user + makes an explicit call to hide(). + + \sa setPersistentOpenGLContext() + */ + +void QQuickCanvas::setPersistentSceneGraph(bool persistent) +{ + Q_D(QQuickCanvas); + d->persistentSceneGraph = persistent; +} + + + +/*! + Returns whether the scene graph nodes and resources can be released as a part + of a call to releaseResources(). + */ + +bool QQuickCanvas::isPersistentSceneGraph() const +{ + Q_D(const QQuickCanvas); + return d->persistentSceneGraph; +} + + + + + +/*! Returns the invisible root item of the scene. A QQuickCanvas always has a single invisible root item. To add items to this canvas, @@ -1618,6 +1715,7 @@ void QQuickCanvasPrivate::cleanupNodesOnShutdown(QQuickItem *item) if (p->extra.isAllocated()) { p->extra->opacityNode = 0; p->extra->clipNode = 0; + p->extra->rootNode = 0; } p->groupNode = 0; diff --git a/src/quick/items/qquickcanvas.h b/src/quick/items/qquickcanvas.h index 4ac9509896..787bb7e3c7 100644 --- a/src/quick/items/qquickcanvas.h +++ b/src/quick/items/qquickcanvas.h @@ -114,6 +114,12 @@ public: void setClearColor(const QColor &color); QColor clearColor() const; + void setPersistentOpenGLContext(bool persistent); + bool isPersistentOpenGLContext() const; + + void setPersistentSceneGraph(bool persistent); + bool isPersistentSceneGraph() const; + QOpenGLContext *openglContext() const; Q_SIGNALS: diff --git a/src/quick/items/qquickcanvas_p.h b/src/quick/items/qquickcanvas_p.h index 147526466e..643d694400 100644 --- a/src/quick/items/qquickcanvas_p.h +++ b/src/quick/items/qquickcanvas_p.h @@ -144,7 +144,7 @@ public: void setFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions = 0); void clearFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions = 0); - void notifyFocusChangesRecur(QQuickItem **item, int remaining); + static void notifyFocusChangesRecur(QQuickItem **item, int remaining); void updateFocusItemTransform(); @@ -184,6 +184,11 @@ public: uint clearBeforeRendering : 1; + // Currently unused in the default implementation, as we're not stopping + // rendering when obscured as we should... + uint persistentGLContext : 1; + uint persistentSceneGraph : 1; + QOpenGLFramebufferObject *renderTarget; uint renderTargetId; QSize renderTargetSize; diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index b6d3ebd7f2..62d0e4aa41 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -887,11 +887,11 @@ void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event) bool stealY = stealMouse; bool stealX = stealMouse; - qint64 elapsed = computeCurrentTime(event) - lastPressTime; + qint64 elapsedSincePress = computeCurrentTime(event) - lastPressTime; if (q->yflick()) { qreal dy = event->localPos().y() - pressPos.y(); - if (qAbs(dy) > qApp->styleHints()->startDragDistance() || elapsed > 200) { + if (qAbs(dy) > qApp->styleHints()->startDragDistance() || elapsedSincePress > 200) { if (!vMoved) vData.dragStartOffset = dy; qreal newY = dy + vData.pressPos - vData.dragStartOffset; @@ -924,7 +924,7 @@ void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event) if (q->xflick()) { qreal dx = event->localPos().x() - pressPos.x(); - if (qAbs(dx) > qApp->styleHints()->startDragDistance() || elapsed > 200) { + if (qAbs(dx) > qApp->styleHints()->startDragDistance() || elapsedSincePress > 200) { if (!hMoved) hData.dragStartOffset = dx; qreal newX = dx + hData.pressPos - hData.dragStartOffset; @@ -974,28 +974,26 @@ void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event) q->movementStarting(); } - if (!lastPos.isNull()) { - qint64 currentTimestamp = computeCurrentTime(event); - qreal elapsed = qreal(currentTimestamp - lastPosTime) / 1000.; - if (elapsed <= 0) - return; - lastPosTime = currentTimestamp; - QQuickMouseEventEx *extended = QQuickMouseEventEx::extended(event); - if (q->yflick() && !rejectY) { - if (extended && extended->capabilities().testFlag(QTouchDevice::Velocity)) { - vData.addVelocitySample(extended->velocity().y(), maxVelocity); - } else { - qreal dy = event->localPos().y()-lastPos.y(); - vData.addVelocitySample(dy/elapsed, maxVelocity); - } + qint64 currentTimestamp = computeCurrentTime(event); + qreal elapsed = qreal(currentTimestamp - (lastPos.isNull() ? lastPressTime : lastPosTime)) / 1000.; + if (elapsed <= 0) + return; + lastPosTime = currentTimestamp; + QQuickMouseEventEx *extended = QQuickMouseEventEx::extended(event); + if (q->yflick() && !rejectY) { + if (extended && extended->capabilities().testFlag(QTouchDevice::Velocity)) { + vData.addVelocitySample(extended->velocity().y(), maxVelocity); + } else { + qreal dy = event->localPos().y() - (lastPos.isNull() ? pressPos.y() : lastPos.y()); + vData.addVelocitySample(dy/elapsed, maxVelocity); } - if (q->xflick() && !rejectX) { - if (extended && extended->capabilities().testFlag(QTouchDevice::Velocity)) { - hData.addVelocitySample(extended->velocity().x(), maxVelocity); - } else { - qreal dx = event->localPos().x()-lastPos.x(); - hData.addVelocitySample(dx/elapsed, maxVelocity); - } + } + if (q->xflick() && !rejectX) { + if (extended && extended->capabilities().testFlag(QTouchDevice::Velocity)) { + hData.addVelocitySample(extended->velocity().x(), maxVelocity); + } else { + qreal dx = event->localPos().x() - (lastPos.isNull() ? pressPos.x() : lastPos.x()); + hData.addVelocitySample(dx/elapsed, maxVelocity); } } @@ -1763,6 +1761,10 @@ void QQuickFlickable::mouseUngrabEvent() d->draggingEnding(); d->stealMouse = false; setKeepMouseGrab(false); + d->fixupX(); + d->fixupY(); + if (!d->timeline.isActive()) + movementEnding(); } } diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 423fb0f40c..2a744c0559 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -1621,6 +1621,36 @@ void QQuickItemPrivate::setAccessibleFlagAndListener() } } +void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus) +{ + Q_Q(QQuickItem); + Q_ASSERT(scope); + + QQuickItemPrivate *scopePrivate = QQuickItemPrivate::get(scope); + + QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem; + // Correct focus chain in scope + if (oldSubFocusItem) { + QQuickItem *sfi = scopePrivate->subFocusItem->parentItem(); + while (sfi && sfi != scope) { + QQuickItemPrivate::get(sfi)->subFocusItem = 0; + sfi = sfi->parentItem(); + } + } + + if (focus) { + scopePrivate->subFocusItem = q; + QQuickItem *sfi = scopePrivate->subFocusItem->parentItem(); + while (sfi && sfi != scope) { + QQuickItemPrivate::get(sfi)->subFocusItem = q; + sfi = sfi->parentItem(); + } + } else { + scopePrivate->subFocusItem = 0; + } +} + + /*! \class QQuickItem \brief The QQuickItem class provides the most basic of all visual items in QML. @@ -1794,10 +1824,13 @@ QQuickItem::~QQuickItem() Q_D(QQuickItem); + if (d->canvasRefCount > 1) + d->canvasRefCount = 1; // Make sure canvas is set to null in next call to derefCanvas(). if (d->parentItem) setParentItem(0); - else if (d->canvas && d->itemNodeInstance) - QQuickCanvasPrivate::get(d->canvas)->cleanup(d->itemNodeInstance); // cleanup root + else if (d->canvas) + d->derefCanvas(); + // XXX todo - optimize while (!d->childItems.isEmpty()) d->childItems.first()->setParentItem(0); @@ -1894,19 +1927,22 @@ void QQuickItem::setParentItem(QQuickItem *parentItem) QQuickItem *scopeItem = 0; - if (d->canvas && hasFocus()) { - scopeItem = oldParentItem; - while (!scopeItem->isFocusScope()) scopeItem = scopeItem->parentItem(); + if (hasFocus()) scopeFocusedItem = this; - } else if (d->canvas && !isFocusScope() && d->subFocusItem) { - scopeItem = oldParentItem; - while (!scopeItem->isFocusScope()) scopeItem = scopeItem->parentItem(); + else if (!isFocusScope() && d->subFocusItem) scopeFocusedItem = d->subFocusItem; - } - if (scopeFocusedItem) - QQuickCanvasPrivate::get(d->canvas)->clearFocusInScope(scopeItem, scopeFocusedItem, + if (scopeFocusedItem) { + scopeItem = oldParentItem; + while (!scopeItem->isFocusScope() && scopeItem->parentItem()) + scopeItem = scopeItem->parentItem(); + if (d->canvas) { + QQuickCanvasPrivate::get(d->canvas)->clearFocusInScope(scopeItem, scopeFocusedItem, QQuickCanvasPrivate::DontChangeFocusProperty); + } else { + QQuickItemPrivate::get(scopeFocusedItem)->updateSubFocusItem(scopeItem, false); + } + } const bool wasVisible = isVisible(); op->removeChild(this); @@ -1917,35 +1953,54 @@ void QQuickItem::setParentItem(QQuickItem *parentItem) QQuickCanvasPrivate::get(d->canvas)->parentlessItems.remove(this); } - d->parentItem = parentItem; - - QQuickCanvas *parentCanvas = parentItem?QQuickItemPrivate::get(parentItem)->canvas:0; - if (d->canvas != parentCanvas) { - QQuickItemPrivate::InitializationState initState; - initState.clear(); - d->initCanvas(&initState, parentCanvas); + QQuickCanvas *oldParentCanvas = oldParentItem ? QQuickItemPrivate::get(oldParentItem)->canvas : 0; + QQuickCanvas *parentCanvas = parentItem ? QQuickItemPrivate::get(parentItem)->canvas : 0; + if (oldParentCanvas == parentCanvas) { + // Avoid freeing and reallocating resources if the canvas stays the same. + d->parentItem = parentItem; + } else { + if (oldParentCanvas) + d->derefCanvas(); + d->parentItem = parentItem; + if (parentCanvas) + d->refCanvas(parentCanvas); } d->dirty(QQuickItemPrivate::ParentChanged); if (d->parentItem) QQuickItemPrivate::get(d->parentItem)->addChild(this); + else if (d->canvas) + QQuickCanvasPrivate::get(d->canvas)->parentlessItems.insert(this); d->setEffectiveVisibleRecur(d->calcEffectiveVisible()); d->setEffectiveEnableRecur(0, d->calcEffectiveEnable()); - if (scopeFocusedItem && d->parentItem && d->canvas) { - // We need to test whether this item becomes scope focused - QQuickItem *scopeItem = 0; - scopeItem = d->parentItem; - while (!scopeItem->isFocusScope()) scopeItem = scopeItem->parentItem(); + if (d->parentItem) { + if (!scopeFocusedItem) { + if (hasFocus()) + scopeFocusedItem = this; + else if (!isFocusScope() && d->subFocusItem) + scopeFocusedItem = d->subFocusItem; + } - if (scopeItem->scopedFocusItem()) { - QQuickItemPrivate::get(scopeFocusedItem)->focus = false; - emit scopeFocusedItem->focusChanged(false); - } else { - QQuickCanvasPrivate::get(d->canvas)->setFocusInScope(scopeItem, scopeFocusedItem, - QQuickCanvasPrivate::DontChangeFocusProperty); + if (scopeFocusedItem) { + // We need to test whether this item becomes scope focused + QQuickItem *scopeItem = d->parentItem; + while (!scopeItem->isFocusScope() && scopeItem->parentItem()) + scopeItem = scopeItem->parentItem(); + + if (scopeItem->scopedFocusItem()) { + QQuickItemPrivate::get(scopeFocusedItem)->focus = false; + emit scopeFocusedItem->focusChanged(false); + } else { + if (d->canvas) { + QQuickCanvasPrivate::get(d->canvas)->setFocusInScope(scopeItem, scopeFocusedItem, + QQuickCanvasPrivate::DontChangeFocusProperty); + } else { + QQuickItemPrivate::get(scopeFocusedItem)->updateSubFocusItem(scopeItem, true); + } + } } } @@ -2129,30 +2184,82 @@ QQuickItem *QQuickItemPrivate::InitializationState::getFocusScope(QQuickItem *it return focusScope; } -void QQuickItemPrivate::initCanvas(InitializationState *state, QQuickCanvas *c) +void QQuickItemPrivate::refCanvas(InitializationState *state, QQuickCanvas *c) { - Q_Q(QQuickItem); + // An item needs a canvas if it is referenced by another item which has a canvas. + // Typically the item is referenced by a parent, but can also be referenced by a + // ShaderEffect or ShaderEffectSource. 'canvasRefCount' counts how many items with + // a canvas is referencing this item. When the reference count goes from zero to one, + // or one to zero, the canvas of this item is updated and propagated to the children. + // As long as the reference count stays above zero, the canvas is unchanged. + // refCanvas() increments the reference count. + // derefCanvas() decrements the reference count. - if (canvas) { - removeFromDirtyList(); - QQuickCanvasPrivate *c = QQuickCanvasPrivate::get(canvas); - if (polishScheduled) - c->itemsToPolish.remove(q); - if (c->mouseGrabberItem == q) - c->mouseGrabberItem = 0; - if ( hoverEnabled ) - c->hoverItems.removeAll(q); - if (itemNodeInstance) - c->cleanup(itemNodeInstance); - if (!parentItem) - c->parentlessItems.remove(q); + Q_Q(QQuickItem); + Q_ASSERT((canvas != 0) == (canvasRefCount > 0)); + Q_ASSERT(c); + if (++canvasRefCount > 1) { + if (c != canvas) + qWarning("QQuickItem: Cannot use same item on different canvases at the same time."); + return; // Canvas already set. } + Q_ASSERT(canvas == 0); canvas = c; - if (canvas && polishScheduled) + if (polishScheduled) QQuickCanvasPrivate::get(canvas)->itemsToPolish.insert(q); + InitializationState _dummy; + InitializationState *childState = state; + + if (q->isFocusScope()) { + _dummy.clear(q); + childState = &_dummy; + } + + if (!parentItem) + QQuickCanvasPrivate::get(canvas)->parentlessItems.insert(q); + + for (int ii = 0; ii < childItems.count(); ++ii) { + QQuickItem *child = childItems.at(ii); + QQuickItemPrivate::get(child)->refCanvas(childState, c); + } + + dirty(Canvas); + + if (extra.isAllocated() && extra->screenAttached) + extra->screenAttached->canvasChanged(c); + itemChange(QQuickItem::ItemSceneChange, c); +} + +void QQuickItemPrivate::derefCanvas() +{ + Q_Q(QQuickItem); + Q_ASSERT((canvas != 0) == (canvasRefCount > 0)); + + if (!canvas) + return; // This can happen when destroying recursive shader effect sources. + + if (--canvasRefCount > 0) + return; // There are still other references, so don't set canvas to null yet. + + q->releaseResources(); + removeFromDirtyList(); + QQuickCanvasPrivate *c = QQuickCanvasPrivate::get(canvas); + if (polishScheduled) + c->itemsToPolish.remove(q); + if (c->mouseGrabberItem == q) + c->mouseGrabberItem = 0; + if ( hoverEnabled ) + c->hoverItems.removeAll(q); + if (itemNodeInstance) + c->cleanup(itemNodeInstance); + if (!parentItem) + c->parentlessItems.remove(q); + + canvas = 0; + itemNodeInstance = 0; if (extra.isAllocated()) { @@ -2165,39 +2272,19 @@ void QQuickItemPrivate::initCanvas(InitializationState *state, QQuickCanvas *c) groupNode = 0; paintNode = 0; - InitializationState _dummy; - InitializationState *childState = state; - - if (c && q->isFocusScope()) { - _dummy.clear(q); - childState = &_dummy; - } - - if (!parentItem && canvas) - QQuickCanvasPrivate::get(canvas)->parentlessItems.insert(q); - for (int ii = 0; ii < childItems.count(); ++ii) { QQuickItem *child = childItems.at(ii); - QQuickItemPrivate::get(child)->initCanvas(childState, c); - } - - if (c && focus) { - // Fixup - if (state->getFocusScope(q)->scopedFocusItem()) { - focus = false; - emit q->focusChanged(false); - } else { - QQuickCanvasPrivate::get(canvas)->setFocusInScope(state->getFocusScope(q), q); - } + QQuickItemPrivate::get(child)->derefCanvas(); } dirty(Canvas); if (extra.isAllocated() && extra->screenAttached) - extra->screenAttached->canvasChanged(c); - itemChange(QQuickItem::ItemSceneChange, c); + extra->screenAttached->canvasChanged(0); + itemChange(QQuickItem::ItemSceneChange, (QQuickCanvas *)0); } + /*! Returns a transform that maps points from canvas space into item space. */ @@ -2300,7 +2387,7 @@ bool QQuickItem::isComponentComplete() const QQuickItemPrivate::QQuickItemPrivate() : _anchors(0), _stateGroup(0), flags(0), widthValid(false), heightValid(false), baselineOffsetValid(false), componentComplete(true), - keepMouse(false), keepTouch(false), hoverEnabled(false), smooth(false), focus(false), activeFocus(false), notifiedFocus(false), + keepMouse(false), keepTouch(false), hoverEnabled(false), smooth(true), focus(false), activeFocus(false), notifiedFocus(false), notifiedActiveFocus(false), filtersChildMouseEvents(false), explicitVisible(true), effectiveVisible(true), explicitEnable(true), effectiveEnable(true), polishScheduled(false), inheritedLayoutMirror(false), effectiveLayoutMirror(false), isMirrorImplicit(true), @@ -2310,7 +2397,7 @@ QQuickItemPrivate::QQuickItemPrivate() dirtyAttributes(0), nextDirtyItem(0), prevDirtyItem(0), - canvas(0), parentItem(0), sortedChildItems(&childItems), + canvas(0), canvasRefCount(0), parentItem(0), sortedChildItems(&childItems), subFocusItem(0), @@ -2954,6 +3041,23 @@ QSGNode *QQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) return 0; } +/*! + This function is called when the item's scene graph resources are no longer needed. + It allows items to free its resources, for instance textures, that are not owned by scene graph + nodes. Note that scene graph nodes are managed by QQuickCanvas and should not be deleted by + this function. Scene graph resources are no longer needed when the parent is set to null and + the item is not used by any \l ShaderEffect or \l ShaderEffectSource. + + This function is called from the main thread. Therefore, resources used by the scene graph + should not be deleted directly, but by calling \l QObject::deleteLater(). + + \note The item destructor still needs to free its scene graph resources if not already done. + */ + +void QQuickItem::releaseResources() +{ +} + QSGTransformNode *QQuickItemPrivate::createTransformNode() { return new QSGTransformNode; @@ -3018,6 +3122,10 @@ void QQuickItem::inputMethodEvent(QInputMethodEvent *event) void QQuickItem::focusInEvent(QFocusEvent *) { +#ifndef QT_NO_ACCESSIBILITY + QAccessibleEvent ev(this, QAccessible::Focus); + QAccessible::updateAccessibility(&ev); +#endif } void QQuickItem::focusOutEvent(QFocusEvent *) @@ -3988,7 +4096,12 @@ bool QQuickItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible) childVisibilityChanged |= QQuickItemPrivate::get(childItems.at(ii))->setEffectiveVisibleRecur(newEffectiveVisible); itemChange(QQuickItem::ItemVisibleHasChanged, effectiveVisible); - +#ifndef QT_NO_ACCESSIBILITY + if (isAccessible) { + QAccessibleEvent ev(q, effectiveVisible ? QAccessible::ObjectShow : QAccessible::ObjectHide); + QAccessible::updateAccessibility(&ev); + } +#endif emit q->visibleChanged(); if (childVisibilityChanged) emit q->visibleChildrenChanged(); @@ -4217,13 +4330,15 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt /*! \property QQuickItem::smooth - \brief whether the item is smoothly transformed. + \brief whether the item is smoothed or not. - This property is provided purely for the purpose of optimization. Turning - smooth transforms off is faster, but looks worse; turning smooth - transformations on is slower, but looks better. + Primarily used in image based elements to decide if the item should use smooth + sampling or not. Smooth sampling is performed using linear interpolation, while + non-smooth is performed using nearest neighbor. - By default smooth transformations are off. + In Qt Quick 2.0, this property has minimal impact on performance. + + By default is true. */ /*! @@ -4650,15 +4765,33 @@ void QQuickItem::setFocus(bool focus) if (d->focus == focus) return; - if (d->canvas) { + if (d->canvas || d->parentItem) { // Need to find our nearest focus scope QQuickItem *scope = parentItem(); - while (scope && !scope->isFocusScope()) + while (scope && !scope->isFocusScope() && scope->parentItem()) scope = scope->parentItem(); - if (focus) - QQuickCanvasPrivate::get(d->canvas)->setFocusInScope(scope, this); - else - QQuickCanvasPrivate::get(d->canvas)->clearFocusInScope(scope, this); + if (d->canvas) { + if (focus) + QQuickCanvasPrivate::get(d->canvas)->setFocusInScope(scope, this); + else + QQuickCanvasPrivate::get(d->canvas)->clearFocusInScope(scope, this); + } else { + // do the focus changes from setFocusInScope/clearFocusInScope that are + // unrelated to a canvas + QVarLengthArray<QQuickItem *, 20> changed; + QQuickItem *oldSubFocusItem = QQuickItemPrivate::get(scope)->subFocusItem; + if (oldSubFocusItem) { + QQuickItemPrivate::get(oldSubFocusItem)->focus = false; + changed << oldSubFocusItem; + } + d->updateSubFocusItem(scope, focus); + + d->focus = focus; + changed << this; + emit focusChanged(focus); + + QQuickCanvasPrivate::notifyFocusChangesRecur(changed.data(), changed.count() - 1); + } } else { d->focus = focus; emit focusChanged(focus); diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h index f14d60b028..70a8ebc932 100644 --- a/src/quick/items/qquickitem.h +++ b/src/quick/items/qquickitem.h @@ -396,6 +396,7 @@ protected: const QRectF &oldGeometry); virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + virtual void releaseResources(); virtual void updatePolish(); protected Q_SLOTS: diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 03fc66eadb..01e8b4d335 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -450,6 +450,7 @@ public: QQuickItem**prevDirtyItem; QQuickCanvas *canvas; + int canvasRefCount; inline QSGContext *sceneGraphContext() const; QQuickItem *parentItem; @@ -472,9 +473,13 @@ public: private: QQuickItem *focusScope; }; - void initCanvas(InitializationState *, QQuickCanvas *); + + void refCanvas(QQuickCanvas *); + void refCanvas(InitializationState *, QQuickCanvas *); + void derefCanvas(); QQuickItem *subFocusItem; + void updateSubFocusItem(QQuickItem *scope, bool focus); QTransform canvasToItemTransform() const; QTransform itemToCanvasTransform() const; @@ -856,6 +861,13 @@ QQuickItem::TransformOrigin QQuickItemPrivate::origin() const return extra.isAllocated()?extra->origin:QQuickItem::Center; } +inline void QQuickItemPrivate::refCanvas(QQuickCanvas *c) +{ + QQuickItemPrivate::InitializationState initState; + initState.clear(); + refCanvas(&initState, c); +} + QSGTransformNode *QQuickItemPrivate::itemNode() { if (!itemNodeInstance) { diff --git a/src/quick/items/qquickitemanimation.cpp b/src/quick/items/qquickitemanimation.cpp index a1c398eeb6..3e84eb6115 100644 --- a/src/quick/items/qquickitemanimation.cpp +++ b/src/quick/items/qquickitemanimation.cpp @@ -927,6 +927,8 @@ QAbstractAnimationJob* QQuickPathAnimation::transition(QQuickStateActions &actio void QQuickPathAnimationUpdater::setValue(qreal v) { + v = qMin(qMax(v, (qreal)0.0), (qreal)1.0);; + if (interruptStart.isValid()) { if (reverse) v = 1 - v; diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index 50a3216bf0..0d95500860 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -1101,9 +1101,9 @@ void QQuickItemView::trackedPositionChanged() if (d->layoutOrientation() == Qt::Vertical) endOffset += d->vData.endMargin; else if (d->isContentFlowReversed()) - endOffset += d->hData.endMargin; - else endOffset += d->hData.startMargin; + else + endOffset += d->hData.endMargin; trackedPos += endOffset; trackedEndPos += endOffset; toItemPos += endOffset; @@ -1204,12 +1204,11 @@ qreal QQuickItemView::minXExtent() const return QQuickFlickable::minXExtent(); if (d->hData.minExtentDirty) { - d->minExtent = -d->startPosition(); + d->minExtent = -d->startPosition() + d->hData.startMargin; qreal highlightStart; qreal highlightEnd; qreal endPositionFirstItem = 0; if (d->isContentFlowReversed()) { - d->minExtent += d->hData.endMargin; if (d->model && d->model->count()) endPositionFirstItem = d->positionAt(d->model->count()-1); else if (d->header) @@ -1222,7 +1221,6 @@ qreal QQuickItemView::minXExtent() const if (d->minExtent < maxX) d->minExtent = maxX; } else { - d->minExtent += d->hData.startMargin; endPositionFirstItem = d->endPositionAt(0); highlightStart = d->highlightRangeStart; highlightEnd = d->highlightRangeEnd; @@ -1279,7 +1277,7 @@ qreal QQuickItemView::maxXExtent() const if (d->isContentFlowReversed()) { if (d->header) d->maxExtent -= d->headerSize(); - d->maxExtent -= d->hData.startMargin; + d->maxExtent -= d->hData.endMargin; } else { if (d->footer) d->maxExtent -= d->footerSize(); @@ -1314,7 +1312,7 @@ qreal QQuickItemView::xOrigin() const { Q_D(const QQuickItemView); if (d->isContentFlowReversed()) - return -maxXExtent() + d->size() - d->hData.startMargin; + return -maxXExtent() + d->size() - d->hData.endMargin; else return -minXExtent() + d->hData.startMargin; } diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp index 59cb37c15d..f41ba44943 100644 --- a/src/quick/items/qquickloader.cpp +++ b/src/quick/items/qquickloader.cpp @@ -61,6 +61,8 @@ QQuickLoaderPrivate::QQuickLoaderPrivate() QQuickLoaderPrivate::~QQuickLoaderPrivate() { + delete itemContext; + itemContext = 0; delete incubator; disposeInitialPropertyValues(); } @@ -79,12 +81,21 @@ void QQuickLoaderPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRec void QQuickLoaderPrivate::clear() { + Q_Q(QQuickLoader); disposeInitialPropertyValues(); if (incubator) incubator->clear(); + delete itemContext; + itemContext = 0; + if (loadingFromSource && component) { + // disconnect since we deleteLater + QObject::disconnect(component, SIGNAL(statusChanged(QQmlComponent::Status)), + q, SLOT(_q_sourceLoaded())); + QObject::disconnect(component, SIGNAL(progressChanged(qreal)), + q, SIGNAL(progressChanged())); component->deleteLater(); component = 0; } @@ -545,6 +556,7 @@ void QQuickLoaderPrivate::setInitialState(QObject *obj) QQml_setParent_noEvent(itemContext, obj); QQml_setParent_noEvent(item, q); item->setParentItem(q); + itemContext = 0; } if (initialPropertyValues.IsEmpty()) diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index 1fa0a90b28..b85a449c51 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -1422,6 +1422,9 @@ void QQuickPathView::mouseUngrabEvent() d->stealMouse = false; setKeepMouseGrab(false); d->lastPosTime.invalidate(); + d->fixOffset(); + if (!d->tl.isActive()) + movementEnding(); } } diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp index d7eedd42b6..e232746417 100644 --- a/src/quick/items/qquickshadereffect.cpp +++ b/src/quick/items/qquickshadereffect.cpp @@ -398,12 +398,27 @@ void QQuickShaderEffect::updateLogAndStatus(const QString &log, int status) emit statusChanged(); } +void QQuickShaderEffect::sourceDestroyed(QObject *object) +{ + for (int i = 0; i < m_sources.size(); ++i) { + SourceData &source = m_sources[i]; + if (object == source.sourceObject) + source.sourceObject = 0; + } +} + void QQuickShaderEffect::setSource(const QVariant &var, int index) { Q_ASSERT(index >= 0 && index < m_sources.size()); SourceData &source = m_sources[index]; + if (source.sourceObject) { + if (canvas()) + QQuickItemPrivate::get(source.sourceObject)->derefCanvas(); + disconnect(source.sourceObject, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*))); + } + source.sourceObject = 0; if (var.isNull()) { return; @@ -425,16 +440,13 @@ void QQuickShaderEffect::setSource(const QVariant &var, int index) source.sourceObject = item; if (item) { - QQuickItemPrivate *d = QQuickItemPrivate::get(item); // 'item' needs a canvas to get a scene graph node. It usually gets one through its // parent, but if the source item is "inline" rather than a reference -- i.e. // "property variant source: Image { }" instead of "property variant source: foo" -- it // will not get a parent. In those cases, 'item' should get the canvas from 'this'. - if (!d->parentItem && canvas() && !d->canvas) { - QQuickItemPrivate::InitializationState initState; - initState.clear(); - d->initCanvas(&initState, canvas()); - } + if (canvas()) + QQuickItemPrivate::get(item)->refCanvas(canvas()); + connect(item, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*))); } } @@ -512,6 +524,11 @@ void QQuickShaderEffect::reset() for (int i = 0; i < m_sources.size(); ++i) { const SourceData &source = m_sources.at(i); delete source.mapper; + if (source.sourceObject) { + if (canvas()) + QQuickItemPrivate::get(source.sourceObject)->derefCanvas(); + disconnect(source.sourceObject, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*))); + } } m_sources.clear(); m_log.clear(); @@ -817,15 +834,22 @@ QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDa } for (int i = 0; i < oldTextures.size(); ++i) { QSGTextureProvider *t = oldTextures.at(i).second; - if (t) + if (t) { disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture())); + disconnect(t, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*))); + } } for (int i = 0; i < m_sources.size(); ++i) { const SourceData &source = m_sources.at(i); QSGTextureProvider *t = source.sourceObject ? source.sourceObject->textureProvider() : 0; textures.append(qMakePair(source.name, t)); - if (t) - connect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()), Qt::DirectConnection); + if (t) { + Q_ASSERT_X(t->thread() == QThread::currentThread(), + "QQuickShaderEffect::updatePaintNode", + "Texture provider must belong to the rendering thread"); + connect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture())); + connect(t, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*))); + } } material->setUniforms(values); material->setTextureProviders(textures); @@ -840,12 +864,15 @@ void QQuickShaderEffect::itemChange(ItemChange change, const ItemChangeData &val { if (change == QQuickItem::ItemSceneChange) { // See comment in QQuickShaderEffect::setSource(). - for (int i = 0; i < m_sources.size(); ++i) { - QQuickItemPrivate *d = QQuickItemPrivate::get(m_sources.at(i).sourceObject); - if (!d->parentItem && value.canvas != d->canvas) { - QQuickItemPrivate::InitializationState initState; - initState.clear(); - d->initCanvas(&initState, value.canvas); + if (value.canvas) { + for (int i = 0; i < m_sources.size(); ++i) { + if (m_sources.at(i).sourceObject) + QQuickItemPrivate::get(m_sources.at(i).sourceObject)->refCanvas(value.canvas); + } + } else { + for (int i = 0; i < m_sources.size(); ++i) { + if (m_sources.at(i).sourceObject) + QQuickItemPrivate::get(m_sources.at(i).sourceObject)->derefCanvas(); } } } diff --git a/src/quick/items/qquickshadereffect_p.h b/src/quick/items/qquickshadereffect_p.h index 4475c22b28..db1e4e78c1 100644 --- a/src/quick/items/qquickshadereffect_p.h +++ b/src/quick/items/qquickshadereffect_p.h @@ -133,6 +133,7 @@ private Q_SLOTS: void updateData(); void updateGeometry(); void updateLogAndStatus(const QString &log, int status); + void sourceDestroyed(QObject *object); private: friend class QQuickCustomMaterialShader; @@ -156,7 +157,7 @@ private: struct SourceData { QSignalMapper *mapper; - QPointer<QQuickItem> sourceObject; + QQuickItem *sourceObject; QByteArray name; }; QVector<SourceData> m_sources; diff --git a/src/quick/items/qquickshadereffectnode.cpp b/src/quick/items/qquickshadereffectnode.cpp index ae61ad940d..c4b91844e0 100644 --- a/src/quick/items/qquickshadereffectnode.cpp +++ b/src/quick/items/qquickshadereffectnode.cpp @@ -376,6 +376,14 @@ void QQuickShaderEffectMaterial::updateTextures() const } } +void QQuickShaderEffectMaterial::invalidateTextureProvider(QSGTextureProvider *provider) +{ + for (int i = 0; i < m_textures.size(); ++i) { + if (provider == m_textures.at(i).second) + m_textures[i].second = 0; + } +} + QQuickShaderEffectNode::QQuickShaderEffectNode() : m_material(this) @@ -397,6 +405,12 @@ void QQuickShaderEffectNode::markDirtyTexture() markDirty(DirtyMaterial); } +void QQuickShaderEffectNode::textureProviderDestroyed(QObject *object) +{ + Q_ASSERT(qobject_cast<QSGTextureProvider *>(object)); + m_material.invalidateTextureProvider(static_cast<QSGTextureProvider *>(object)); +} + void QQuickShaderEffectNode::preprocess() { Q_ASSERT(material()); diff --git a/src/quick/items/qquickshadereffectnode_p.h b/src/quick/items/qquickshadereffectnode_p.h index fc47f626e1..e22d2de9e2 100644 --- a/src/quick/items/qquickshadereffectnode_p.h +++ b/src/quick/items/qquickshadereffectnode_p.h @@ -102,6 +102,7 @@ public: void setTextureProviders(const QVector<QPair<QByteArray, QSGTextureProvider *> > &textures); const QVector<QPair<QByteArray, QSGTextureProvider *> > &textureProviders() const; void updateTextures() const; + void invalidateTextureProvider(QSGTextureProvider *provider); protected: friend class QQuickCustomMaterialShader; @@ -143,6 +144,7 @@ Q_SIGNALS: private Q_SLOTS: void markDirtyTexture(); + void textureProviderDestroyed(QObject *object); private: QQuickShaderEffectMaterial m_material; diff --git a/src/quick/items/qquickshadereffectsource.cpp b/src/quick/items/qquickshadereffectsource.cpp index 33776be712..c55b1ca7f5 100644 --- a/src/quick/items/qquickshadereffectsource.cpp +++ b/src/quick/items/qquickshadereffectsource.cpp @@ -54,6 +54,39 @@ QT_BEGIN_NAMESPACE DEFINE_BOOL_CONFIG_OPTION(qmlFboOverlay, QML_FBO_OVERLAY) +namespace +{ + class BindableFbo : public QSGBindable + { + public: + BindableFbo(QOpenGLFramebufferObject *fbo, QSGDepthStencilBuffer *depthStencil); + virtual ~BindableFbo(); + virtual void bind() const; + private: + QOpenGLFramebufferObject *m_fbo; + QSGDepthStencilBuffer *m_depthStencil; + }; + + BindableFbo::BindableFbo(QOpenGLFramebufferObject *fbo, QSGDepthStencilBuffer *depthStencil) + : m_fbo(fbo) + , m_depthStencil(depthStencil) + { + } + + BindableFbo::~BindableFbo() + { + if (m_depthStencil) + m_depthStencil->detach(); + } + + void BindableFbo::bind() const + { + m_fbo->bind(); + if (m_depthStencil) + m_depthStencil->attach(); + } +} + class QQuickShaderEffectSourceTextureProvider : public QSGTextureProvider { Q_OBJECT @@ -239,6 +272,7 @@ void QQuickShaderEffectTexture::grab() delete m_fbo; delete m_secondaryFbo; m_fbo = m_secondaryFbo = 0; + m_depthStencilBuffer.clear(); m_dirtyTexture = false; if (m_grab) emit scheduledUpdateCompleted(); @@ -272,13 +306,12 @@ void QQuickShaderEffectTexture::grab() delete m_secondaryFbo; QOpenGLFramebufferObjectFormat format; - format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); format.setInternalTextureFormat(m_format); format.setSamples(8); m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format); + m_depthStencilBuffer = m_context->depthStencilBufferForFbo(m_secondaryFbo); } else { QOpenGLFramebufferObjectFormat format; - format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); format.setInternalTextureFormat(m_format); format.setMipmap(m_mipmap); if (m_recursive) { @@ -287,6 +320,7 @@ void QQuickShaderEffectTexture::grab() m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format); glBindTexture(GL_TEXTURE_2D, m_secondaryFbo->texture()); updateBindOptions(true); + m_depthStencilBuffer = m_context->depthStencilBufferForFbo(m_secondaryFbo); } else { delete m_fbo; delete m_secondaryFbo; @@ -294,6 +328,7 @@ void QQuickShaderEffectTexture::grab() m_secondaryFbo = 0; glBindTexture(GL_TEXTURE_2D, m_fbo->texture()); updateBindOptions(true); + m_depthStencilBuffer = m_context->depthStencilBufferForFbo(m_fbo); } } } @@ -336,7 +371,7 @@ void QQuickShaderEffectTexture::grab() m_renderer->setClearColor(Qt::transparent); if (m_multisampling) { - m_renderer->renderScene(QSGBindableFbo(m_secondaryFbo)); + m_renderer->renderScene(BindableFbo(m_secondaryFbo, m_depthStencilBuffer.data())); if (deleteFboLater) { delete m_fbo; @@ -354,7 +389,7 @@ void QQuickShaderEffectTexture::grab() QOpenGLFramebufferObject::blitFramebuffer(m_fbo, r, m_secondaryFbo, r); } else { if (m_recursive) { - m_renderer->renderScene(QSGBindableFbo(m_secondaryFbo)); + m_renderer->renderScene(BindableFbo(m_secondaryFbo, m_depthStencilBuffer.data())); if (deleteFboLater) { delete m_fbo; @@ -368,7 +403,7 @@ void QQuickShaderEffectTexture::grab() } qSwap(m_fbo, m_secondaryFbo); } else { - m_renderer->renderScene(QSGBindableFbo(m_fbo)); + m_renderer->renderScene(BindableFbo(m_fbo, m_depthStencilBuffer.data())); } } @@ -504,6 +539,8 @@ QQuickShaderEffectSource::~QQuickShaderEffectSource() QQuickItemPrivate *sd = QQuickItemPrivate::get(m_sourceItem); sd->removeItemChangeListener(this, QQuickItemPrivate::Geometry); sd->derefFromEffectItem(m_hideSource); + if (canvas()) + sd->derefCanvas(); } } @@ -599,6 +636,9 @@ void QQuickShaderEffectSource::setSourceItem(QQuickItem *item) QQuickItemPrivate *d = QQuickItemPrivate::get(m_sourceItem); d->derefFromEffectItem(m_hideSource); d->removeItemChangeListener(this, QQuickItemPrivate::Geometry); + disconnect(m_sourceItem, SIGNAL(destroyed(QObject*)), this, SLOT(sourceItemDestroyed(QObject*))); + if (canvas()) + d->derefCanvas(); } m_sourceItem = item; @@ -608,18 +648,25 @@ void QQuickShaderEffectSource::setSourceItem(QQuickItem *item) // parent, but if the source item is "inline" rather than a reference -- i.e. // "sourceItem: Item { }" instead of "sourceItem: foo" -- it will not get a parent. // In those cases, 'item' should get the canvas from 'this'. - if (!d->parentItem && canvas() && !d->canvas) { - QQuickItemPrivate::InitializationState initState; - initState.clear(); - d->initCanvas(&initState, canvas()); - } + if (canvas()) + d->refCanvas(canvas()); d->refFromEffectItem(m_hideSource); d->addItemChangeListener(this, QQuickItemPrivate::Geometry); + connect(m_sourceItem, SIGNAL(destroyed(QObject*)), this, SLOT(sourceItemDestroyed(QObject*))); } update(); emit sourceItemChanged(); } +void QQuickShaderEffectSource::sourceItemDestroyed(QObject *item) +{ + Q_ASSERT(item == m_sourceItem); + m_sourceItem = 0; + update(); + emit sourceItemChanged(); +} + + /*! \qmlproperty rect ShaderEffectSource::sourceRect @@ -841,22 +888,35 @@ static void get_wrap_mode(QQuickShaderEffectSource::WrapMode mode, QSGTexture::W } +void QQuickShaderEffectSource::releaseResources() +{ + if (m_texture) { + m_texture->deleteLater(); + m_texture = 0; + } + if (m_provider) { + m_provider->deleteLater(); + m_provider = 0; + } +} + QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) { if (!m_sourceItem || m_sourceItem->width() == 0 || m_sourceItem->height() == 0) { + if (m_texture) + m_texture->setItem(0); delete oldNode; return 0; } ensureTexture(); - QQuickShaderEffectTexture *tex = qobject_cast<QQuickShaderEffectTexture *>(m_texture); - tex->setLive(m_live); - tex->setItem(QQuickItemPrivate::get(m_sourceItem)->itemNode()); + m_texture->setLive(m_live); + m_texture->setItem(QQuickItemPrivate::get(m_sourceItem)->itemNode()); QRectF sourceRect = m_sourceRect.width() == 0 || m_sourceRect.height() == 0 ? QRectF(0, 0, m_sourceItem->width(), m_sourceItem->height()) : m_sourceRect; - tex->setRect(sourceRect); + m_texture->setRect(sourceRect); QSize textureSize = m_textureSize.isEmpty() ? QSize(qCeil(qAbs(sourceRect.width())), qCeil(qAbs(sourceRect.height()))) : m_textureSize; @@ -869,13 +929,13 @@ QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaint while (textureSize.height() < minTextureSize.height()) textureSize.rheight() *= 2; - tex->setSize(textureSize); - tex->setRecursive(m_recursive); - tex->setFormat(GLenum(m_format)); - tex->setHasMipmaps(m_mipmap); + m_texture->setSize(textureSize); + m_texture->setRecursive(m_recursive); + m_texture->setFormat(GLenum(m_format)); + m_texture->setHasMipmaps(m_mipmap); if (m_grab) - tex->scheduleUpdate(); + m_texture->scheduleUpdate(); m_grab = false; QSGTexture::Filtering filtering = QQuickItemPrivate::get(this)->smooth @@ -924,12 +984,10 @@ void QQuickShaderEffectSource::itemChange(ItemChange change, const ItemChangeDat { if (change == QQuickItem::ItemSceneChange && m_sourceItem) { // See comment in QQuickShaderEffectSource::setSourceItem(). - QQuickItemPrivate *d = QQuickItemPrivate::get(m_sourceItem); - if (!d->parentItem && value.canvas != d->canvas) { - QQuickItemPrivate::InitializationState initState; - initState.clear(); - d->initCanvas(&initState, value.canvas); - } + if (value.canvas) + QQuickItemPrivate::get(m_sourceItem)->refCanvas(value.canvas); + else + QQuickItemPrivate::get(m_sourceItem)->derefCanvas(); } QQuickItem::itemChange(change, value); } diff --git a/src/quick/items/qquickshadereffectsource_p.h b/src/quick/items/qquickshadereffectsource_p.h index 793e89cd69..0853394339 100644 --- a/src/quick/items/qquickshadereffectsource_p.h +++ b/src/quick/items/qquickshadereffectsource_p.h @@ -136,6 +136,7 @@ private: QSGRenderer *m_renderer; QOpenGLFramebufferObject *m_fbo; QOpenGLFramebufferObject *m_secondaryFbo; + QSharedPointer<QSGDepthStencilBuffer> m_depthStencilBuffer; #ifdef QSG_DEBUG_FBO_OVERLAY QSGRectangleNode *m_debugOverlay; @@ -228,7 +229,11 @@ Q_SIGNALS: void scheduledUpdateCompleted(); +private Q_SLOTS: + void sourceItemDestroyed(QObject *item); + protected: + virtual void releaseResources(); virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); virtual void itemGeometryChanged(QQuickItem *item, const QRectF &newRect, const QRectF &oldRect); @@ -240,7 +245,7 @@ private: QQuickShaderEffectSourceTextureProvider *m_provider; QQuickShaderEffectTexture *m_texture; WrapMode m_wrapMode; - QPointer<QQuickItem> m_sourceItem; + QQuickItem *m_sourceItem; QRectF m_sourceRect; QSize m_textureSize; Format m_format; diff --git a/src/quick/items/qquicksprite.cpp b/src/quick/items/qquicksprite.cpp index e0535ed77e..724bf8fef1 100644 --- a/src/quick/items/qquicksprite.cpp +++ b/src/quick/items/qquicksprite.cpp @@ -248,12 +248,14 @@ int QQuickSprite::variedDuration() const //Deals with precedence when multiple d + (m_frameDurationVariation * ((qreal)qrand()/RAND_MAX) * 2) - m_frameDurationVariation; return qMax(0, m_frames * mspf); - } - qWarning() << "Sprite::duration is changing meaning to the full animation duration."; - qWarning() << "Use Sprite::frameDuration for the old meaning, of per frame duration."; - qWarning() << "As an interim measure, duration/durationVariation means the same as frameDuration/frameDurationVariation, and you'll get this warning spewed out everywhere to movtivate you."; + } else if (duration() >= 0) { + qWarning() << "Sprite::duration is changing meaning to the full animation duration."; + qWarning() << "Use Sprite::frameDuration for the old meaning, of per frame duration."; + qWarning() << "As an interim measure, duration/durationVariation means the same as frameDuration/frameDurationVariation, and you'll get this warning spewed out everywhere to motivate you."; //Note that the spammyness is due to this being the best location to detect, but also called once each animation loop - return QQuickStochasticState::variedDuration() * m_frames; + return QQuickStochasticState::variedDuration() * m_frames; + } + return 1000; //When nothing set } void QQuickSprite::startImageLoading() diff --git a/src/quick/items/qquickspriteengine.cpp b/src/quick/items/qquickspriteengine.cpp index 1c35688c29..d4ddbc400d 100644 --- a/src/quick/items/qquickspriteengine.cpp +++ b/src/quick/items/qquickspriteengine.cpp @@ -411,9 +411,9 @@ QImage QQuickSpriteEngine::assembledImage() int frameWidth = state->m_frameWidth; int frameHeight = state->m_frameHeight; if (img.height() == frameHeight && img.width() < maxSize){//Simple case - p.drawImage(0,y,img); + p.drawImage(0,y,img.copy(state->m_frameX,0,state->m_frames * frameWidth, frameHeight)); + state->m_rowStartX = 0; state->m_rowY = y; - state->m_rowStartX = state->m_frameX;//In case it was offset, but we took the simple route of not chopping out the other bits y += frameHeight; }else{//Chopping up image case state->m_framesPerRow = image.width()/frameWidth; diff --git a/src/quick/items/qquickspriteengine_p.h b/src/quick/items/qquickspriteengine_p.h index 3763509462..00cefbfbc0 100644 --- a/src/quick/items/qquickspriteengine_p.h +++ b/src/quick/items/qquickspriteengine_p.h @@ -70,7 +70,7 @@ class Q_AUTOTEST_EXPORT QQuickStochasticState : public QObject //Currently for i public: QQuickStochasticState(QObject* parent = 0) : QObject(parent) - , m_duration(1000) + , m_duration(-1) , m_durationVariation(0) , m_randomStart(false) { diff --git a/src/quick/items/qquickspriteimage.cpp b/src/quick/items/qquickspriteimage.cpp index 6edb3ad8c3..2a151d02a8 100644 --- a/src/quick/items/qquickspriteimage.cpp +++ b/src/quick/items/qquickspriteimage.cpp @@ -261,7 +261,7 @@ struct SpriteVertices { The sprite or sprites to draw. Sprites will be scaled to the size of this element. */ -//TODO: Implicitly size element to size of first sprite? +//TODO: Implicitly size element to size of first sprite? or currentSprite? QQuickSpriteImage::QQuickSpriteImage(QQuickItem *parent) : QQuickItem(parent) , m_node(0) diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 9f22dfdd08..d7303352c5 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -421,6 +421,10 @@ void QQuickTextPrivate::updateSize() //setup instance of QTextLayout for all cases other than richtext if (!richText) { QRectF textRect = setupTextLayout(&naturalWidth); + + if (internalWidthUpdate) // probably the result of a binding loop, but by letting it + return; // get this far we'll get a warning to that effect if it is. + layedOutTextRect = textRect; size = textRect.size(); dy -= size.height(); @@ -443,7 +447,13 @@ void QQuickTextPrivate::updateSize() if (requireImplicitWidth && q->widthValid()) { extra->doc->setTextWidth(-1); naturalWidth = extra->doc->idealWidth(); + const bool wasInLayout = internalWidthUpdate; + internalWidthUpdate = true; + q->setImplicitWidth(naturalWidth); + internalWidthUpdate = wasInLayout; } + if (internalWidthUpdate) + return; if (wrapMode != QQuickText::NoWrap && q->widthValid()) extra->doc->setTextWidth(q->width()); else @@ -468,8 +478,6 @@ void QQuickTextPrivate::updateSize() qreal iWidth = -1; if (!q->widthValid()) iWidth = size.width(); - else if (requireImplicitWidth) - iWidth = naturalWidth; if (iWidth > -1) q->setImplicitSize(iWidth, size.height()); internalWidthUpdate = false; @@ -697,6 +705,11 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth) layout.endLayout(); *naturalWidth = layout.maximumWidth(); layout.clearLayout(); + + bool wasInLayout = internalWidthUpdate; + internalWidthUpdate = true; + q->setImplicitWidth(*naturalWidth); + internalWidthUpdate = wasInLayout; } QFontMetrics fm(font); @@ -704,7 +717,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth) return QRect(0, 0, 0, height); } - const qreal lineWidth = q->widthValid() ? q->width() : FLT_MAX; + qreal lineWidth = q->widthValid() && q->width() > 0 ? q->width() : FLT_MAX; const qreal maxHeight = q->heightValid() ? q->height() : FLT_MAX; const bool customLayout = isLineLaidOutConnected(); @@ -735,6 +748,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth) QTextLine line; int visibleCount = 0; bool elide; + bool widthChanged; qreal height = 0; QString elideText; bool once = true; @@ -755,13 +769,14 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth) scaledFont.setPointSize(scaledFontSize); layout.setFont(scaledFont); } - layout.beginLayout(); + layout.beginLayout(); bool wrapped = false; bool truncateHeight = false; truncated = false; elide = false; + widthChanged = false; int characterCount = 0; int unwrappedLineCount = 1; int maxLineCount = maximumLineCount(); @@ -864,7 +879,6 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth) br = br.united(line.naturalTextRect()); line = nextLine; } - layout.endLayout(); br.moveTop(0); @@ -886,8 +900,21 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth) if (!line.isValid()) break; } + *naturalWidth = qMax(*naturalWidth, widthLayout.maximumWidth()); } + + bool wasInLayout = internalWidthUpdate; + internalWidthUpdate = true; + q->setImplicitWidth(*naturalWidth); + internalWidthUpdate = wasInLayout; + + const qreal oldWidth = lineWidth; + lineWidth = q->widthValid() && q->width() > 0 ? q->width() : FLT_MAX; + if (lineWidth != oldWidth && (singlelineElide || multilineElide || canWrap || horizontalFit)) { + widthChanged = true; + continue; + } } // If the next needs to be elided and there's an abbreviated string available @@ -911,32 +938,36 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth) if (horizontalFit) { if (unelidedRect.width() > lineWidth || (!verticalFit && wrapped)) { largeFont = scaledFontSize - 1; - scaledFontSize = (smallFont + largeFont) / 2; if (smallFont > largeFont) break; + scaledFontSize = (smallFont + largeFont) / 2; + if (pixelSize) + scaledFont.setPixelSize(scaledFontSize); + else + scaledFont.setPointSize(scaledFontSize); continue; } else if (!verticalFit) { smallFont = scaledFontSize; - scaledFontSize = (smallFont + largeFont + 1) / 2; if (smallFont == largeFont) break; + scaledFontSize = (smallFont + largeFont + 1) / 2; } } if (verticalFit) { if (truncateHeight || unelidedRect.height() > maxHeight) { largeFont = scaledFontSize - 1; - scaledFontSize = (smallFont + largeFont + 1) / 2; if (smallFont > largeFont) break; + scaledFontSize = (smallFont + largeFont) / 2; + } else { smallFont = scaledFontSize; - scaledFontSize = (smallFont + largeFont + 1) / 2; if (smallFont == largeFont) break; + scaledFontSize = (smallFont + largeFont + 1) / 2; } } - } if (eos != multilengthEos) diff --git a/src/quick/items/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp index 9a61312910..1846d03b9b 100644 --- a/src/quick/items/qquicktextcontrol.cpp +++ b/src/quick/items/qquicktextcontrol.cpp @@ -531,29 +531,12 @@ void QQuickTextControlPrivate::extendWordwiseSelection(int suggestedNewPosition, if (!wordSelectionEnabled && (mouseXPosition < wordStartX || mouseXPosition > wordEndX)) return; - if (wordSelectionEnabled) { - if (suggestedNewPosition < selectedWordOnDoubleClick.position()) { - cursor.setPosition(selectedWordOnDoubleClick.selectionEnd()); - setCursorPosition(wordStartPos, QTextCursor::KeepAnchor); - } else { - cursor.setPosition(selectedWordOnDoubleClick.selectionStart()); - setCursorPosition(wordEndPos, QTextCursor::KeepAnchor); - } + if (suggestedNewPosition < selectedWordOnDoubleClick.position()) { + cursor.setPosition(selectedWordOnDoubleClick.selectionEnd()); + setCursorPosition(wordStartPos, QTextCursor::KeepAnchor); } else { - // keep the already selected word even when moving to the left - // (#39164) - if (suggestedNewPosition < selectedWordOnDoubleClick.position()) - cursor.setPosition(selectedWordOnDoubleClick.selectionEnd()); - else - cursor.setPosition(selectedWordOnDoubleClick.selectionStart()); - - const qreal differenceToStart = mouseXPosition - wordStartX; - const qreal differenceToEnd = wordEndX - mouseXPosition; - - if (differenceToStart < differenceToEnd) - setCursorPosition(wordStartPos, QTextCursor::KeepAnchor); - else - setCursorPosition(wordEndPos, QTextCursor::KeepAnchor); + cursor.setPosition(selectedWordOnDoubleClick.selectionStart()); + setCursorPosition(wordEndPos, QTextCursor::KeepAnchor); } if (interactionFlags & Qt::TextSelectableByMouse) { @@ -594,13 +577,6 @@ void QQuickTextControlPrivate::extendBlockwiseSelection(int suggestedNewPosition } } -void QQuickTextControlPrivate::_q_deleteSelected() -{ - if (!(interactionFlags & Qt::TextEditable) || !cursor.hasSelection()) - return; - cursor.removeSelectedText(); -} - void QQuickTextControl::undo() { Q_D(QQuickTextControl); @@ -690,14 +666,6 @@ void QQuickTextControl::paste(QClipboard::Mode mode) } #endif -void QQuickTextControl::clear() -{ - Q_D(QQuickTextControl); - // clears and sets empty content - d->setContent(); -} - - void QQuickTextControl::selectAll() { Q_D(QQuickTextControl); @@ -1527,13 +1495,6 @@ QVariant QQuickTextControl::inputMethodQuery(Qt::InputMethodQuery property) cons } } -void QQuickTextControl::setFocus(bool focus, Qt::FocusReason reason) -{ - QFocusEvent ev(focus ? QEvent::FocusIn : QEvent::FocusOut, - reason); - processEvent(&ev); -} - void QQuickTextControlPrivate::focusEvent(QFocusEvent *e) { Q_Q(QQuickTextControl); @@ -1555,31 +1516,6 @@ void QQuickTextControlPrivate::focusEvent(QFocusEvent *e) } } -QString QQuickTextControlPrivate::anchorForCursor(const QTextCursor &anchorCursor) const -{ - if (anchorCursor.hasSelection()) { - QTextCursor cursor = anchorCursor; - if (cursor.selectionStart() != cursor.position()) - cursor.setPosition(cursor.selectionStart()); - cursor.movePosition(QTextCursor::NextCharacter); - QTextCharFormat fmt = cursor.charFormat(); - if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) - return fmt.stringProperty(QTextFormat::AnchorHref); - } - return QString(); -} - -QTextCursor QQuickTextControl::cursorForPosition(const QPointF &pos) const -{ - Q_D(const QQuickTextControl); - int cursorPos = hitTest(pos, Qt::FuzzyHit); - if (cursorPos == -1) - cursorPos = 0; - QTextCursor c(d->doc); - c.setPosition(cursorPos); - return c; -} - QRectF QQuickTextControl::cursorRect(const QTextCursor &cursor) const { Q_D(const QQuickTextControl); @@ -1609,23 +1545,6 @@ QString QQuickTextControl::anchorAt(const QPointF &pos) const return d->doc->documentLayout()->anchorAt(pos); } -QString QQuickTextControl::anchorAtCursor() const -{ - Q_D(const QQuickTextControl); - - return d->anchorForCursor(d->cursor); -} - -int QQuickTextControl::cursorWidth() const -{ -#ifndef QT_NO_PROPERTIES - Q_D(const QQuickTextControl); - return d->doc->documentLayout()->property("cursorWidth").toInt(); -#else - return 1; -#endif -} - void QQuickTextControl::setCursorWidth(int width) { Q_D(QQuickTextControl); @@ -1639,36 +1558,12 @@ void QQuickTextControl::setCursorWidth(int width) d->repaintCursor(); } -bool QQuickTextControl::acceptRichText() const -{ - Q_D(const QQuickTextControl); - return d->acceptRichText; -} - void QQuickTextControl::setAcceptRichText(bool accept) { Q_D(QQuickTextControl); d->acceptRichText = accept; } -void QQuickTextControl::setTextWidth(qreal width) -{ - Q_D(QQuickTextControl); - d->doc->setTextWidth(width); -} - -qreal QQuickTextControl::textWidth() const -{ - Q_D(const QQuickTextControl); - return d->doc->textWidth(); -} - -QSizeF QQuickTextControl::size() const -{ - Q_D(const QQuickTextControl); - return d->doc->size(); -} - void QQuickTextControl::moveCursor(QTextCursor::MoveOperation op, QTextCursor::MoveMode mode) { Q_D(QQuickTextControl); @@ -1700,24 +1595,12 @@ void QQuickTextControl::setCursorIsFocusIndicator(bool b) d->repaintCursor(); } -bool QQuickTextControl::cursorIsFocusIndicator() const -{ - Q_D(const QQuickTextControl); - return d->cursorIsFocusIndicator; -} - void QQuickTextControl::setWordSelectionEnabled(bool enabled) { Q_D(QQuickTextControl); d->wordSelectionEnabled = enabled; } -bool QQuickTextControl::isWordSelectionEnabled() const -{ - Q_D(const QQuickTextControl); - return d->wordSelectionEnabled; -} - QMimeData *QQuickTextControl::createMimeDataFromSelection() const { Q_D(const QQuickTextControl); diff --git a/src/quick/items/qquicktextcontrol_p.h b/src/quick/items/qquicktextcontrol_p.h index 97ecdc4c6e..9e3fc90eb1 100644 --- a/src/quick/items/qquicktextcontrol_p.h +++ b/src/quick/items/qquicktextcontrol_p.h @@ -80,12 +80,6 @@ class Q_AUTOTEST_EXPORT QQuickTextControl : public QObject { Q_OBJECT Q_DECLARE_PRIVATE(QQuickTextControl) -#ifndef QT_NO_TEXTHTMLPARSER - Q_PROPERTY(QString html READ toHtml WRITE setHtml NOTIFY textChanged USER true) -#endif - Q_PROPERTY(bool acceptRichText READ acceptRichText WRITE setAcceptRichText) - Q_PROPERTY(int cursorWidth READ cursorWidth WRITE setCursorWidth) - Q_PROPERTY(Qt::TextInteractionFlags textInteractionFlags READ textInteractionFlags WRITE setTextInteractionFlags) public: explicit QQuickTextControl(QTextDocument *doc, QObject *parent = 0); virtual ~QQuickTextControl(); @@ -104,7 +98,6 @@ public: QString toHtml() const; #endif - QTextCursor cursorForPosition(const QPointF &pos) const; QRectF cursorRect(const QTextCursor &cursor) const; QRectF cursorRect() const; QRectF selectionRect(const QTextCursor &cursor) const; @@ -112,26 +105,15 @@ public: QString anchorAt(const QPointF &pos) const; - QString anchorAtCursor() const; - - int cursorWidth() const; void setCursorWidth(int width); - bool acceptRichText() const; void setAcceptRichText(bool accept); - void setTextWidth(qreal width); - qreal textWidth() const; - QSizeF size() const; - void moveCursor(QTextCursor::MoveOperation op, QTextCursor::MoveMode mode = QTextCursor::MoveAnchor); bool canPaste() const; void setCursorIsFocusIndicator(bool b); - bool cursorIsFocusIndicator() const; - - bool isWordSelectionEnabled() const; void setWordSelectionEnabled(bool enabled); virtual int hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const; @@ -151,7 +133,6 @@ public Q_SLOTS: void undo(); void redo(); - void clear(); void selectAll(); Q_SIGNALS: @@ -175,9 +156,6 @@ public: virtual void processEvent(QEvent *e, const QMatrix &matrix); void processEvent(QEvent *e, const QPointF &coordinateOffset = QPointF()); - // control methods - void setFocus(bool focus, Qt::FocusReason = Qt::OtherFocusReason); - virtual QVariant inputMethodQuery(Qt::InputMethodQuery property) const; virtual QMimeData *createMimeDataFromSelection() const; @@ -195,7 +173,6 @@ private: Q_DISABLE_COPY(QQuickTextControl) Q_PRIVATE_SLOT(d_func(), void _q_updateCurrentCharFormatAndSelection()) Q_PRIVATE_SLOT(d_func(), void _q_emitCursorPosChanged(const QTextCursor &)) - Q_PRIVATE_SLOT(d_func(), void _q_deleteSelected()) Q_PRIVATE_SLOT(d_func(), void _q_updateBlock(const QTextBlock &)) Q_PRIVATE_SLOT(d_func(), void _q_documentLayoutChanged()) }; diff --git a/src/quick/items/qquicktextcontrol_p_p.h b/src/quick/items/qquicktextcontrol_p_p.h index 44bc00221b..9d776ce46b 100644 --- a/src/quick/items/qquicktextcontrol_p_p.h +++ b/src/quick/items/qquicktextcontrol_p_p.h @@ -106,8 +106,6 @@ public: void extendWordwiseSelection(int suggestedNewPosition, qreal mouseXPosition); void extendBlockwiseSelection(int suggestedNewPosition); - void _q_deleteSelected(); - void _q_setCursorAfterUndoRedo(int undoPosition, int charsAdded, int charsRemoved); QRectF cursorRectPlusUnicodeDirectionMarkers(const QTextCursor &cursor) const; @@ -116,8 +114,6 @@ public: inline QRectF selectionRect() const { return selectionRect(this->cursor); } - QString anchorForCursor(const QTextCursor &anchor) const; - void keyPressEvent(QKeyEvent *e); void mousePressEvent(QMouseEvent *event, const QPointF &pos); void mouseMoveEvent(QMouseEvent *event, const QPointF &pos); diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index 5456d3523a..4fa5233b9a 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -1143,7 +1143,8 @@ void QQuickTextEdit::setInputMethodHints(Qt::InputMethodHints hints) void QQuickTextEdit::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { - if (newGeometry.width() != oldGeometry.width()) + Q_D(QQuickTextEdit); + if (newGeometry.width() != oldGeometry.width() && d->wrapMode != NoWrap && !d->inLayout) updateSize(); QQuickImplicitSizeItem::geometryChanged(newGeometry, oldGeometry); } @@ -1477,6 +1478,7 @@ Handles the given mouse \a event. void QQuickTextEdit::mousePressEvent(QMouseEvent *event) { Q_D(QQuickTextEdit); + d->control->processEvent(event, QPointF(0, -d->yoff)); if (d->focusOnPress){ bool hadActiveFocus = hasActiveFocus(); forceActiveFocus(); @@ -1484,7 +1486,6 @@ void QQuickTextEdit::mousePressEvent(QMouseEvent *event) if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) openSoftwareInputPanel(); } - d->control->processEvent(event, QPointF(0, -d->yoff)); if (!event->isAccepted()) QQuickImplicitSizeItem::mousePressEvent(event); } @@ -1857,6 +1858,13 @@ void QQuickTextEdit::updateSize() if (d->requireImplicitWidth) { d->document->setTextWidth(-1); naturalWidth = d->document->idealWidth(); + + const bool wasInLayout = d->inLayout; + d->inLayout = true; + setImplicitWidth(naturalWidth); + d->inLayout = wasInLayout; + if (d->inLayout) // probably the result of a binding loop, but by letting it + return; // get this far we'll get a warning to that effect. } if (d->document->textWidth() != width()) d->document->setTextWidth(width()); @@ -1888,11 +1896,11 @@ void QQuickTextEdit::updateSize() d->document->setTextWidth(newWidth); // ### Text does not align if width is not set (QTextDoc bug) // ### Setting the implicitWidth triggers another updateSize(), and unless there are bindings nothing has changed. qreal iWidth = -1; - if (!widthValid()) + if (!widthValid() && !d->requireImplicitWidth) iWidth = newWidth; - else if (d->requireImplicitWidth) - iWidth = naturalWidth; + qreal newHeight = d->document->isEmpty() ? fm.height() : d->document->size().height(); + if (iWidth > -1) setImplicitSize(iWidth, newHeight); else diff --git a/src/quick/items/qquicktextedit_p_p.h b/src/quick/items/qquicktextedit_p_p.h index 055b5c7929..f0a35d5266 100644 --- a/src/quick/items/qquicktextedit_p_p.h +++ b/src/quick/items/qquicktextedit_p_p.h @@ -78,7 +78,7 @@ public: , documentDirty(true), dirty(false), richText(false), cursorVisible(false) , focusOnPress(true), persistentSelection(false), requireImplicitWidth(false) , selectByMouse(false), canPaste(false), canPasteValid(false), hAlignImplicit(true) - , rightToLeftText(false), textCached(false) + , rightToLeftText(false), textCached(false), inLayout(false) { } @@ -144,6 +144,7 @@ public: bool hAlignImplicit:1; bool rightToLeftText:1; bool textCached:1; + bool inLayout:1; }; QT_END_NAMESPACE diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index 0b21d6b169..94856b63ab 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -1444,13 +1444,9 @@ void QQuickTextInput::mousePressEvent(QMouseEvent *event) d->pressPos = event->localPos(); - if (d->focusOnPress) { - bool hadActiveFocus = hasActiveFocus(); - forceActiveFocus(); - // re-open input panel on press if already focused - if (hasActiveFocus() && hadActiveFocus && !d->m_readOnly) - openSoftwareInputPanel(); - } + if (d->sendMouseEventToInputContext(event)) + return; + if (d->selectByMouse) { setKeepMouseGrab(false); d->selectPressed = true; @@ -1463,12 +1459,18 @@ void QQuickTextInput::mousePressEvent(QMouseEvent *event) } } - if (d->sendMouseEventToInputContext(event)) - return; - bool mark = (event->modifiers() & Qt::ShiftModifier) && d->selectByMouse; int cursor = d->positionAt(event->localPos()); d->moveCursor(cursor, mark); + + if (d->focusOnPress) { + bool hadActiveFocus = hasActiveFocus(); + forceActiveFocus(); + // re-open input panel on press if already focused + if (hasActiveFocus() && hadActiveFocus && !d->m_readOnly) + openSoftwareInputPanel(); + } + event->setAccepted(true); } @@ -1602,9 +1604,11 @@ void QQuickTextInput::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { Q_D(QQuickTextInput); - if (newGeometry.width() != oldGeometry.width()) - d->updateLayout(); - updateCursorRectangle(); + if (!d->inLayout) { + if (newGeometry.width() != oldGeometry.width() && d->wrapMode != NoWrap) + d->updateLayout(); + updateCursorRectangle(); + } QQuickImplicitSizeItem::geometryChanged(newGeometry, oldGeometry); } @@ -1614,14 +1618,19 @@ void QQuickTextInputPrivate::updateHorizontalScroll() QTextLine currentLine = m_textLayout.lineForTextPosition(m_cursor + m_preeditCursor); const int preeditLength = m_textLayout.preeditAreaText().length(); const qreal width = qMax<qreal>(0, q->width()); - qreal widthUsed = currentLine.isValid() ? currentLine.naturalTextWidth() : 0; + qreal cix = 0; + qreal widthUsed = 0; + if (currentLine.isValid()) { + cix = currentLine.cursorToX(m_cursor + preeditLength); + const qreal cursorWidth = cix >= 0 ? cix : width - cix; + widthUsed = qMax(currentLine.naturalTextWidth(), cursorWidth); + } int previousScroll = hscroll; if (!autoScroll || widthUsed <= width || m_echoMode == QQuickTextInput::NoEcho) { hscroll = 0; } else { Q_ASSERT(currentLine.isValid()); - qreal cix = currentLine.cursorToX(m_cursor + preeditLength); if (cix - hscroll >= width) { // text doesn't fit, cursor is to the right of br (scroll right) hscroll = cix - width; @@ -1632,6 +1641,10 @@ void QQuickTextInputPrivate::updateHorizontalScroll() // text doesn't fit, text document is to the left of br; align // right hscroll = widthUsed - width; + } else if (width - hscroll > widthUsed) { + // text doesn't fit, text document is to the right of br; align + // left + hscroll = width - widthUsed; } if (preeditLength > 0) { // check to ensure long pre-edit text doesn't push the cursor @@ -2688,6 +2701,38 @@ void QQuickTextInputPrivate::updateDisplayText(bool forceUpdate) } } +qreal QQuickTextInputPrivate::getImplicitWidth() const +{ + Q_Q(const QQuickTextInput); + if (!requireImplicitWidth) { + QQuickTextInputPrivate *d = const_cast<QQuickTextInputPrivate *>(this); + d->requireImplicitWidth = true; + + if (q->isComponentComplete()) { + // One time cost, only incurred if implicitWidth is first requested after + // componentComplete. + QTextLayout layout(m_text); + + QTextOption option = m_textLayout.textOption(); + option.setTextDirection(m_layoutDirection); + option.setFlags(QTextOption::IncludeTrailingSpaces); + option.setWrapMode(QTextOption::WrapMode(wrapMode)); + option.setAlignment(Qt::Alignment(q->effectiveHAlign())); + layout.setTextOption(option); + layout.setFont(font); + layout.setPreeditArea(m_textLayout.preeditAreaPosition(), m_textLayout.preeditAreaText()); + layout.beginLayout(); + + QTextLine line = layout.createLine(); + line.setLineWidth(INT_MAX); + d->implicitWidth = qCeil(line.naturalTextWidth()); + + layout.endLayout(); + } + } + return implicitWidth; +} + void QQuickTextInputPrivate::updateLayout() { Q_Q(QQuickTextInput); @@ -2699,7 +2744,6 @@ void QQuickTextInputPrivate::updateLayout() QTextOption option = m_textLayout.textOption(); option.setTextDirection(layoutDirection()); - option.setFlags(QTextOption::IncludeTrailingSpaces); option.setWrapMode(QTextOption::WrapMode(wrapMode)); option.setAlignment(Qt::Alignment(q->effectiveHAlign())); m_textLayout.setTextOption(option); @@ -2708,9 +2752,17 @@ void QQuickTextInputPrivate::updateLayout() boundingRect = QRectF(); m_textLayout.beginLayout(); QTextLine line = m_textLayout.createLine(); + if (requireImplicitWidth) { + line.setLineWidth(INT_MAX); + const bool wasInLayout = inLayout; + inLayout = true; + q->setImplicitWidth(qCeil(line.naturalTextWidth())); + inLayout = wasInLayout; + if (inLayout) // probably the result of a binding loop, but by letting it + return; // get this far we'll get a warning to that effect. + } qreal lineWidth = q->widthValid() ? q->width() : INT_MAX; qreal height = 0; - QTextLine firstLine = line; do { line.setLineWidth(lineWidth); line.setPosition(QPointF(line.position().x(), height)); @@ -2728,7 +2780,11 @@ void QQuickTextInputPrivate::updateLayout() updateType = UpdatePaintNode; q->update(); - q->setImplicitSize(boundingRect.width(), boundingRect.height()); + + if (!requireImplicitWidth && !q->widthValid()) + q->setImplicitSize(qCeil(boundingRect.width()), qCeil(boundingRect.height())); + else + q->setImplicitHeight(qCeil(boundingRect.height())); if (previousRect != boundingRect) emit q->contentSizeChanged(); @@ -3261,7 +3317,14 @@ void QQuickTextInputPrivate::internalSetText(const QString &txt, int pos, bool e m_textDirty = (oldText != m_text); bool changed = finishChange(-1, true, edited); +#ifdef QT_NO_ACCESSIBILITY Q_UNUSED(changed) +#else + if (changed) { + QAccessibleTextUpdateEvent ev(q, 0, oldText, m_text); + QAccessible::updateAccessibility(&ev); + } +#endif } @@ -3885,6 +3948,11 @@ bool QQuickTextInputPrivate::emitCursorPositionChanged() } } +#ifndef QT_NO_ACCESSIBILITY + QAccessibleTextCursorEvent ev(q, m_cursor); + QAccessible::updateAccessibility(&ev); +#endif + return true; } return false; @@ -3964,12 +4032,10 @@ void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event) } #ifndef QT_NO_SHORTCUT else if (event == QKeySequence::Undo) { - if (!m_readOnly) - q->undo(); + q->undo(); } else if (event == QKeySequence::Redo) { - if (!m_readOnly) - q->redo(); + q->redo(); } else if (event == QKeySequence::SelectAll) { selectAll(); diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h index bb00600661..165155acd0 100644 --- a/src/quick/items/qquicktextinput_p_p.h +++ b/src/quick/items/qquicktextinput_p_p.h @@ -125,6 +125,8 @@ public: , m_acceptableInput(1) , m_blinkStatus(0) , m_passwordEchoEditing(false) + , inLayout(false) + , requireImplicitWidth(false) { } @@ -256,7 +258,8 @@ public: bool m_acceptableInput : 1; bool m_blinkStatus : 1; bool m_passwordEchoEditing : 1; - + bool inLayout:1; + bool requireImplicitWidth:1; static inline QQuickTextInputPrivate *get(QQuickTextInput *t) { return t->d_func(); @@ -404,6 +407,8 @@ public: void updateLayout(); + qreal getImplicitWidth() const; + private: void removeSelectedText(); void internalSetText(const QString &txt, int pos = -1, bool edited = true); diff --git a/src/quick/items/qquickvisualadaptormodel.cpp b/src/quick/items/qquickvisualadaptormodel.cpp index 622adf4ce9..cd9db7235b 100644 --- a/src/quick/items/qquickvisualadaptormodel.cpp +++ b/src/quick/items/qquickvisualadaptormodel.cpp @@ -65,7 +65,8 @@ public: } VDMDelegateDataType(const VDMDelegateDataType &type) - : metaObject(0) + : QQmlRefCount() + , metaObject(0) , propertyCache(0) , propertyOffset(type.propertyOffset) , signalOffset(type.signalOffset) diff --git a/src/quick/items/qquickwindowmanager.cpp b/src/quick/items/qquickwindowmanager.cpp index 64eb2bf53b..61c2ef24b4 100644 --- a/src/quick/items/qquickwindowmanager.cpp +++ b/src/quick/items/qquickwindowmanager.cpp @@ -228,7 +228,7 @@ public slots: private: void handleAddedWindows(); void handleAddedWindow(QQuickCanvas *canvas); - void handleRemovedWindows(); + void handleRemovedWindows(bool clearGLContext = true); QSGContext *sg; QOpenGLContext *gl; @@ -475,7 +475,7 @@ void QQuickRenderThreadSingleContextWindowManager::hide(QQuickCanvas *canvas) /*! Called on Render Thread */ -void QQuickRenderThreadSingleContextWindowManager::handleRemovedWindows() +void QQuickRenderThreadSingleContextWindowManager::handleRemovedWindows(bool clearGLContext) { #ifdef THREAD_DEBUG printf(" RenderThread: about to remove %d\n", m_removed_windows.size()); @@ -496,7 +496,7 @@ void QQuickRenderThreadSingleContextWindowManager::handleRemovedWindows() // If a window is removed because it has been hidden it will take with it // the gl context (at least on Mac) if bound, so disconnect the gl context // from anything - if (removedAnything) + if (removedAnything && clearGLContext) gl->doneCurrent(); } @@ -755,7 +755,7 @@ void QQuickRenderThreadSingleContextWindowManager::run() #endif m_removed_windows << m_rendered_windows.keys(); - handleRemovedWindows(); + handleRemovedWindows(false); sg->invalidate(); diff --git a/src/quick/particles/qquickangledirection.cpp b/src/quick/particles/qquickangledirection.cpp index a3bd45e0bf..e77c47362c 100644 --- a/src/quick/particles/qquickangledirection.cpp +++ b/src/quick/particles/qquickangledirection.cpp @@ -42,6 +42,9 @@ #include "qquickangledirection_p.h" #include <stdlib.h> #include <cmath> +#ifdef Q_OS_QNX +#include <math.h> +#endif QT_BEGIN_NAMESPACE const qreal CONV = 0.017453292519943295; /*! diff --git a/src/quick/particles/qquickcustomparticle.cpp b/src/quick/particles/qquickcustomparticle.cpp index a8f146ad05..8f75672e32 100644 --- a/src/quick/particles/qquickcustomparticle.cpp +++ b/src/quick/particles/qquickcustomparticle.cpp @@ -139,6 +139,12 @@ QQuickCustomParticle::QQuickCustomParticle(QQuickItem* parent) class QQuickShaderEffectMaterialObject : public QObject, public QQuickShaderEffectMaterial { }; +void QQuickCustomParticle::sceneGraphInvalidated() +{ + m_nodes.clear(); + m_rootNode = 0; +} + QQuickCustomParticle::~QQuickCustomParticle() { if (m_material) diff --git a/src/quick/particles/qquickcustomparticle_p.h b/src/quick/particles/qquickcustomparticle_p.h index 29f3d19657..e04ac704d0 100644 --- a/src/quick/particles/qquickcustomparticle_p.h +++ b/src/quick/particles/qquickcustomparticle_p.h @@ -93,6 +93,8 @@ protected: QQuickShaderEffectNode *buildCustomNodes(); void performPendingResize(); + void sceneGraphInvalidated(); + private: void buildData(); diff --git a/src/quick/particles/qquickellipseextruder.cpp b/src/quick/particles/qquickellipseextruder.cpp index 3eb547fd2f..083564e5cb 100644 --- a/src/quick/particles/qquickellipseextruder.cpp +++ b/src/quick/particles/qquickellipseextruder.cpp @@ -42,6 +42,11 @@ #include "qquickellipseextruder_p.h" #include <stdlib.h> #include <cmath> + +#ifdef Q_OS_QNX +#include <math.h> +#endif + QT_BEGIN_NAMESPACE /*! \qmlclass EllipseShape QQuickEllipseExtruder diff --git a/src/quick/particles/qquickimageparticle.cpp b/src/quick/particles/qquickimageparticle.cpp index 386892a12a..d9eb6ed01b 100644 --- a/src/quick/particles/qquickimageparticle.cpp +++ b/src/quick/particles/qquickimageparticle.cpp @@ -839,6 +839,13 @@ QQmlListProperty<QQuickSprite> QQuickImageParticle::sprites() return QQmlListProperty<QQuickSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear); } +void QQuickImageParticle::sceneGraphInvalidated() +{ + m_nodes.clear(); + m_rootNode = 0; + m_material = 0; +} + void QQuickImageParticle::setImage(const QUrl &image) { if (image.isEmpty()){ diff --git a/src/quick/particles/qquickimageparticle_p.h b/src/quick/particles/qquickimageparticle_p.h index dca524bcab..4db2c9801a 100644 --- a/src/quick/particles/qquickimageparticle_p.h +++ b/src/quick/particles/qquickimageparticle_p.h @@ -348,6 +348,8 @@ protected: void prepareNextFrame(); void buildParticleNodes(); + void sceneGraphInvalidated(); + private slots: void createEngine(); //### method invoked by sprite list changing (in engine.h) - pretty nasty diff --git a/src/quick/particles/qquickparticleemitter.cpp b/src/quick/particles/qquickparticleemitter.cpp index 0f7f3817f2..035d66cbcd 100644 --- a/src/quick/particles/qquickparticleemitter.cpp +++ b/src/quick/particles/qquickparticleemitter.cpp @@ -284,23 +284,17 @@ QQuickParticleExtruder* QQuickParticleEmitter::effectiveExtruder() void QQuickParticleEmitter::pulse(int milliseconds) { - if (!particleCount()) - qWarning() << "pulse called on an emitter with a particle count of zero"; if (!m_enabled) m_pulseLeft = milliseconds; } void QQuickParticleEmitter::burst(int num) { - if (!particleCount()) - qWarning() << "burst called on an emitter with a particle count of zero"; m_burstQueue << qMakePair(num, QPointF(x(), y())); } void QQuickParticleEmitter::burst(int num, qreal x, qreal y) { - if (!particleCount()) - qWarning() << "burst called on an emitter with a particle count of zero"; m_burstQueue << qMakePair(num, QPointF(x, y)); } diff --git a/src/quick/particles/qquickparticlepainter.cpp b/src/quick/particles/qquickparticlepainter.cpp index f46f2f2235..e490b70240 100644 --- a/src/quick/particles/qquickparticlepainter.cpp +++ b/src/quick/particles/qquickparticlepainter.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qquickparticlepainter_p.h" +#include <QQuickCanvas> #include <QDebug> QT_BEGIN_NAMESPACE /*! @@ -65,10 +66,22 @@ QT_BEGIN_NAMESPACE */ QQuickParticlePainter::QQuickParticlePainter(QQuickItem *parent) : QQuickItem(parent), - m_system(0), m_count(0), m_pleaseReset(true) + m_system(0), m_count(0), m_pleaseReset(true), m_canvas(0) { } +void QQuickParticlePainter::itemChange(ItemChange change, const ItemChangeData &data) +{ + if (change == QQuickItem::ItemSceneChange) { + if (m_canvas) + disconnect(m_canvas, SIGNAL(sceneGraphInvalidated()), this, SLOT(sceneGraphInvalidated())); + m_canvas = data.canvas; + if (m_canvas) + connect(m_canvas, SIGNAL(sceneGraphInvalidated()), this, SLOT(sceneGraphInvalidated()), Qt::DirectConnection); + + } +} + void QQuickParticlePainter::componentComplete() { if (!m_system && qobject_cast<QQuickParticleSystem*>(parentItem())) diff --git a/src/quick/particles/qquickparticlepainter_p.h b/src/quick/particles/qquickparticlepainter_p.h index ebe76d98ea..1ae4625856 100644 --- a/src/quick/particles/qquickparticlepainter_p.h +++ b/src/quick/particles/qquickparticlepainter_p.h @@ -76,6 +76,8 @@ public: return m_groups; } + void itemChange(ItemChange, const ItemChangeData &); + signals: void countChanged(); void systemChanged(QQuickParticleSystem* arg); @@ -96,6 +98,9 @@ public slots: void calcSystemOffset(bool resetPending = false); +private slots: + virtual void sceneGraphInvalidated() {} + protected: /* Reset resets all your internal data structures. But anything attached to a particle should be in attached data. So reset + reloads should have no visible effect. @@ -121,6 +126,8 @@ protected: QStringList m_groups; QPointF m_systemOffset; + QQuickCanvas *m_canvas; + private: QSet<QPair<int,int> > m_pendingCommits; }; diff --git a/src/quick/scenegraph/coreapi/qsggeometry.cpp b/src/quick/scenegraph/coreapi/qsggeometry.cpp index dbac95acd1..cf39c308e7 100644 --- a/src/quick/scenegraph/coreapi/qsggeometry.cpp +++ b/src/quick/scenegraph/coreapi/qsggeometry.cpp @@ -46,6 +46,10 @@ #include <qopenglfunctions.h> #include <private/qopenglextensions_p.h> +#ifdef Q_OS_QNX +#include <malloc.h> +#endif + QT_BEGIN_NAMESPACE diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp index 9608ebe861..c78e243dcd 100644 --- a/src/quick/scenegraph/qsgadaptationlayer.cpp +++ b/src/quick/scenegraph/qsgadaptationlayer.cpp @@ -45,31 +45,28 @@ #include <QtQuick/private/qsgdistancefieldutil_p.h> #include <QtQuick/private/qsgdistancefieldglyphnode_p.h> #include <private/qrawfont_p.h> -#include <private/qdistancefield_p.h> #include <QtGui/qguiapplication.h> #include <qdir.h> QT_BEGIN_NAMESPACE -QHash<QString, QOpenGLMultiGroupSharedResource> QSGDistanceFieldGlyphCache::m_caches_data; +QSGDistanceFieldGlyphCache::Texture QSGDistanceFieldGlyphCache::s_emptyTexture; QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font) : ctx(c) , m_manager(man) + , m_pendingGlyphs(64) { Q_ASSERT(font.isValid()); - m_font = font; - m_cacheData = cacheData(); - - QRawFontPrivate *fontD = QRawFontPrivate::get(m_font); + QRawFontPrivate *fontD = QRawFontPrivate::get(font); m_glyphCount = fontD->fontEngine->glyphCount(); - m_cacheData->doubleGlyphResolution = qt_fontHasNarrowOutlines(font) && m_glyphCount < QT_DISTANCEFIELD_HIGHGLYPHCOUNT; + m_doubleGlyphResolution = qt_fontHasNarrowOutlines(font) && m_glyphCount < QT_DISTANCEFIELD_HIGHGLYPHCOUNT; - m_referenceFont = m_font; - m_referenceFont.setPixelSize(QT_DISTANCEFIELD_BASEFONTSIZE(m_cacheData->doubleGlyphResolution)); + m_referenceFont = font; + m_referenceFont.setPixelSize(QT_DISTANCEFIELD_BASEFONTSIZE(m_doubleGlyphResolution)); Q_ASSERT(m_referenceFont.isValid()); } @@ -77,58 +74,31 @@ QSGDistanceFieldGlyphCache::~QSGDistanceFieldGlyphCache() { } -QSGDistanceFieldGlyphCache::GlyphCacheData *QSGDistanceFieldGlyphCache::cacheData() -{ - QString key = QString::fromLatin1("%1_%2_%3_%4") - .arg(m_font.familyName()) - .arg(m_font.styleName()) - .arg(m_font.weight()) - .arg(m_font.style()); - return m_caches_data[key].value<QSGDistanceFieldGlyphCache::GlyphCacheData>(ctx); -} - -qreal QSGDistanceFieldGlyphCache::fontScale() const +QSGDistanceFieldGlyphCache::GlyphData &QSGDistanceFieldGlyphCache::glyphData(glyph_t glyph) { - return qreal(m_font.pixelSize()) / QT_DISTANCEFIELD_BASEFONTSIZE(m_cacheData->doubleGlyphResolution); -} - -int QSGDistanceFieldGlyphCache::distanceFieldRadius() const -{ - return QT_DISTANCEFIELD_DEFAULT_RADIUS / QT_DISTANCEFIELD_SCALE(m_cacheData->doubleGlyphResolution); -} - -QSGDistanceFieldGlyphCache::Metrics QSGDistanceFieldGlyphCache::glyphMetrics(glyph_t glyph) -{ - QHash<glyph_t, Metrics>::iterator metric = m_metrics.find(glyph); - if (metric == m_metrics.end()) { - QPainterPath path = m_font.pathForGlyph(glyph); - QRectF br = path.boundingRect(); - - Metrics m; - m.width = br.width(); - m.height = br.height(); - m.baselineX = br.x(); - m.baselineY = -br.y(); - - metric = m_metrics.insert(glyph, m); + QHash<glyph_t, GlyphData>::iterator data = m_glyphsData.find(glyph); + if (data == m_glyphsData.end()) { + GlyphData gd; + gd.texture = &s_emptyTexture; + QPainterPath path = m_referenceFont.pathForGlyph(glyph); + gd.boundingRect = path.boundingRect(); + data = m_glyphsData.insert(glyph, gd); } - - return metric.value(); + return data.value(); } -QSGDistanceFieldGlyphCache::TexCoord QSGDistanceFieldGlyphCache::glyphTexCoord(glyph_t glyph) const +QSGDistanceFieldGlyphCache::Metrics QSGDistanceFieldGlyphCache::glyphMetrics(glyph_t glyph, qreal pixelSize) { - return m_cacheData->texCoords.value(glyph); -} + GlyphData &gd = glyphData(glyph); + qreal scale = fontScale(pixelSize); -static QSGDistanceFieldGlyphCache::Texture g_emptyTexture; + Metrics m; + m.width = gd.boundingRect.width() * scale; + m.height = gd.boundingRect.height() * scale; + m.baselineX = gd.boundingRect.x() * scale; + m.baselineY = -gd.boundingRect.y() * scale; -const QSGDistanceFieldGlyphCache::Texture *QSGDistanceFieldGlyphCache::glyphTexture(glyph_t glyph) const -{ - QHash<glyph_t, Texture*>::const_iterator it = m_cacheData->glyphTextures.find(glyph); - if (it == m_cacheData->glyphTextures.constEnd()) - return &g_emptyTexture; - return it.value(); + return m; } void QSGDistanceFieldGlyphCache::populate(const QVector<glyph_t> &glyphs) @@ -143,23 +113,18 @@ void QSGDistanceFieldGlyphCache::populate(const QVector<glyph_t> &glyphs) continue; } - ++m_cacheData->glyphRefCount[glyphIndex]; + GlyphData &gd = glyphData(glyphIndex); + ++gd.ref; referencedGlyphs.insert(glyphIndex); - if (m_cacheData->texCoords.contains(glyphIndex) || newGlyphs.contains(glyphIndex)) + if (gd.texCoord.isValid() || newGlyphs.contains(glyphIndex)) continue; - QPainterPath path = m_referenceFont.pathForGlyph(glyphIndex); - m_cacheData->glyphPaths.insert(glyphIndex, path); - if (path.isEmpty()) { - TexCoord c; - c.width = 0; - c.height = 0; - m_cacheData->texCoords.insert(glyphIndex, c); - continue; - } + gd.texCoord.width = 0; + gd.texCoord.height = 0; - newGlyphs.insert(glyphIndex); + if (!gd.boundingRect.isEmpty()) + newGlyphs.insert(glyphIndex); } if (newGlyphs.isEmpty()) @@ -175,7 +140,8 @@ void QSGDistanceFieldGlyphCache::release(const QVector<glyph_t> &glyphs) int count = glyphs.count(); for (int i = 0; i < count; ++i) { glyph_t glyphIndex = glyphs.at(i); - if (--m_cacheData->glyphRefCount[glyphIndex] == 0 && !glyphTexCoord(glyphIndex).isNull()) + GlyphData &gd = glyphData(glyphIndex); + if (--gd.ref == 0 && !gd.texCoord.isNull()) unusedGlyphs.insert(glyphIndex); } releaseGlyphs(unusedGlyphs); @@ -183,7 +149,7 @@ void QSGDistanceFieldGlyphCache::release(const QVector<glyph_t> &glyphs) void QSGDistanceFieldGlyphCache::update() { - if (m_cacheData->pendingGlyphs.isEmpty()) + if (m_pendingGlyphs.isEmpty()) return; QHash<glyph_t, QImage> distanceFields; @@ -194,16 +160,16 @@ void QSGDistanceFieldGlyphCache::update() QString tmpPath = QString::fromLatin1("%1/.qt/").arg(QDir::tempPath()); QString keyBase = QString::fromLatin1("%1%2%3_%4_%5_%6.fontblob") .arg(tmpPath) - .arg(m_font.familyName()) - .arg(m_font.styleName()) - .arg(m_font.weight()) - .arg(m_font.style()); + .arg(m_referenceFont.familyName()) + .arg(m_referenceFont.styleName()) + .arg(m_referenceFont.weight()) + .arg(m_referenceFont.style()); if (cacheDistanceFields && !QFile::exists(tmpPath)) QDir(tmpPath).mkpath(tmpPath); - for (int i = 0; i < m_cacheData->pendingGlyphs.size(); ++i) { - glyph_t glyphIndex = m_cacheData->pendingGlyphs.at(i); + for (int i = 0; i < m_pendingGlyphs.size(); ++i) { + glyph_t glyphIndex = m_pendingGlyphs.at(i); if (cacheDistanceFields) { QString key = keyBase.arg(glyphIndex); @@ -219,7 +185,7 @@ void QSGDistanceFieldGlyphCache::update() } } - QImage distanceField = qt_renderDistanceFieldGlyph(m_font, glyphIndex, m_cacheData->doubleGlyphResolution); + QImage distanceField = qt_renderDistanceFieldGlyph(m_referenceFont, glyphIndex, m_doubleGlyphResolution); distanceFields.insert(glyphIndex, distanceField); if (cacheDistanceFields) { @@ -230,7 +196,7 @@ void QSGDistanceFieldGlyphCache::update() } } - m_cacheData->pendingGlyphs.reset(); + m_pendingGlyphs.reset(); storeGlyphs(distanceFields); } @@ -242,28 +208,22 @@ void QSGDistanceFieldGlyphCache::setGlyphsPosition(const QList<GlyphPosition> &g int count = glyphs.count(); for (int i = 0; i < count; ++i) { GlyphPosition glyph = glyphs.at(i); + GlyphData &gd = glyphData(glyph.glyph); - Q_ASSERT(m_cacheData->glyphPaths.contains(glyph.glyph)); - - QPainterPath path = m_cacheData->glyphPaths.value(glyph.glyph); - QRectF br = path.boundingRect(); - TexCoord c; - c.xMargin = QT_DISTANCEFIELD_RADIUS(m_cacheData->doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(m_cacheData->doubleGlyphResolution)); - c.yMargin = QT_DISTANCEFIELD_RADIUS(m_cacheData->doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(m_cacheData->doubleGlyphResolution)); - c.x = glyph.position.x(); - c.y = glyph.position.y(); - c.width = br.width(); - c.height = br.height(); - - if (m_cacheData->texCoords.contains(glyph.glyph)) + if (!gd.texCoord.isNull()) invalidatedGlyphs.append(glyph.glyph); - m_cacheData->texCoords.insert(glyph.glyph, c); + gd.texCoord.xMargin = QT_DISTANCEFIELD_RADIUS(m_doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution)); + gd.texCoord.yMargin = QT_DISTANCEFIELD_RADIUS(m_doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution)); + gd.texCoord.x = glyph.position.x(); + gd.texCoord.y = glyph.position.y(); + gd.texCoord.width = gd.boundingRect.width(); + gd.texCoord.height = gd.boundingRect.height(); } if (!invalidatedGlyphs.isEmpty()) { - QLinkedList<QSGDistanceFieldGlyphNode *>::iterator it = m_cacheData->m_registeredNodes.begin(); - while (it != m_cacheData->m_registeredNodes.end()) { + QLinkedList<QSGDistanceFieldGlyphConsumer *>::iterator it = m_registeredNodes.begin(); + while (it != m_registeredNodes.end()) { (*it)->invalidateGlyphs(invalidatedGlyphs); ++it; } @@ -287,28 +247,29 @@ void QSGDistanceFieldGlyphCache::processPendingGlyphs() void QSGDistanceFieldGlyphCache::setGlyphsTexture(const QVector<glyph_t> &glyphs, const Texture &tex) { - int i = m_cacheData->textures.indexOf(tex); + int i = m_textures.indexOf(tex); if (i == -1) { - m_cacheData->textures.append(tex); - i = m_cacheData->textures.size() - 1; + m_textures.append(tex); + i = m_textures.size() - 1; } else { - m_cacheData->textures[i].size = tex.size; + m_textures[i].size = tex.size; } - Texture *texture = &(m_cacheData->textures[i]); + Texture *texture = &(m_textures[i]); QVector<quint32> invalidatedGlyphs; int count = glyphs.count(); for (int j = 0; j < count; ++j) { glyph_t glyphIndex = glyphs.at(j); - if (m_cacheData->glyphTextures.contains(glyphIndex)) + GlyphData &gd = glyphData(glyphIndex); + if (gd.texture != &s_emptyTexture) invalidatedGlyphs.append(glyphIndex); - m_cacheData->glyphTextures.insert(glyphIndex, texture); + gd.texture = texture; } if (!invalidatedGlyphs.isEmpty()) { - QLinkedList<QSGDistanceFieldGlyphNode *>::iterator it = m_cacheData->m_registeredNodes.begin(); - while (it != m_cacheData->m_registeredNodes.end()) { + QLinkedList<QSGDistanceFieldGlyphConsumer*>::iterator it = m_registeredNodes.begin(); + while (it != m_registeredNodes.end()) { (*it)->invalidateGlyphs(invalidatedGlyphs); ++it; } @@ -319,20 +280,14 @@ void QSGDistanceFieldGlyphCache::markGlyphsToRender(const QVector<glyph_t> &glyp { int count = glyphs.count(); for (int i = 0; i < count; ++i) - m_cacheData->pendingGlyphs.add(glyphs.at(i)); -} - -void QSGDistanceFieldGlyphCache::removeGlyph(glyph_t glyph) -{ - m_cacheData->texCoords.remove(glyph); - m_cacheData->glyphTextures.remove(glyph); + m_pendingGlyphs.add(glyphs.at(i)); } void QSGDistanceFieldGlyphCache::updateTexture(GLuint oldTex, GLuint newTex, const QSize &newTexSize) { - int count = m_cacheData->textures.count(); + int count = m_textures.count(); for (int i = 0; i < count; ++i) { - Texture &tex = m_cacheData->textures[i]; + Texture &tex = m_textures[i]; if (tex.textureId == oldTex) { tex.textureId = newTex; tex.size = newTexSize; @@ -341,20 +296,4 @@ void QSGDistanceFieldGlyphCache::updateTexture(GLuint oldTex, GLuint newTex, con } } -bool QSGDistanceFieldGlyphCache::containsGlyph(glyph_t glyph) const -{ - return m_cacheData->texCoords.contains(glyph); -} - -void QSGDistanceFieldGlyphCache::registerGlyphNode(QSGDistanceFieldGlyphNode *node) -{ - m_cacheData->m_registeredNodes.append(node); -} - -void QSGDistanceFieldGlyphCache::unregisterGlyphNode(QSGDistanceFieldGlyphNode *node) -{ - m_cacheData->m_registeredNodes.removeOne(node); -} - - QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h index 47dfdb4d8c..77cd814ce8 100644 --- a/src/quick/scenegraph/qsgadaptationlayer_p.h +++ b/src/quick/scenegraph/qsgadaptationlayer_p.h @@ -54,6 +54,7 @@ #include <private/qfontengine_p.h> #include <QtGui/private/qdatabuffer_p.h> #include <private/qopenglcontext_p.h> +#include <private/qdistancefield_p.h> // ### remove #include <QtQuick/private/qquicktext_p.h> @@ -133,6 +134,14 @@ protected: QQuickItem *m_ownerElement; }; +class Q_QUICK_EXPORT QSGDistanceFieldGlyphConsumer +{ +public: + virtual ~QSGDistanceFieldGlyphConsumer() {} + + virtual void invalidateGlyphs(const QVector<quint32> &glyphs) = 0; +}; + class Q_QUICK_EXPORT QSGDistanceFieldGlyphCache { public: @@ -172,24 +181,30 @@ public: const QSGDistanceFieldGlyphCacheManager *manager() const { return m_manager; } - const QRawFont &font() const { return m_font; } + const QRawFont &referenceFont() const { return m_referenceFont; } - qreal fontScale() const; - int distanceFieldRadius() const; + qreal fontScale(qreal pixelSize) const + { + return pixelSize / QT_DISTANCEFIELD_BASEFONTSIZE(m_doubleGlyphResolution); + } + int distanceFieldRadius() const + { + return QT_DISTANCEFIELD_DEFAULT_RADIUS / QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution); + } int glyphCount() const { return m_glyphCount; } - bool doubleGlyphResolution() const { return m_cacheData->doubleGlyphResolution; } + bool doubleGlyphResolution() const { return m_doubleGlyphResolution; } - Metrics glyphMetrics(glyph_t glyph); - TexCoord glyphTexCoord(glyph_t glyph) const; - const Texture *glyphTexture(glyph_t glyph) const; + Metrics glyphMetrics(glyph_t glyph, qreal pixelSize); + inline TexCoord glyphTexCoord(glyph_t glyph); + inline const Texture *glyphTexture(glyph_t glyph); void populate(const QVector<glyph_t> &glyphs); void release(const QVector<glyph_t> &glyphs); void update(); - void registerGlyphNode(QSGDistanceFieldGlyphNode *node); - void unregisterGlyphNode(QSGDistanceFieldGlyphNode *node); + void registerGlyphNode(QSGDistanceFieldGlyphConsumer *node) { m_registeredNodes.append(node); } + void unregisterGlyphNode(QSGDistanceFieldGlyphConsumer *node) { m_registeredNodes.removeOne(node); } virtual void registerOwnerElement(QQuickItem *ownerElement); virtual void unregisterOwnerElement(QQuickItem *ownerElement); @@ -209,57 +224,64 @@ protected: void setGlyphsPosition(const QList<GlyphPosition> &glyphs); void setGlyphsTexture(const QVector<glyph_t> &glyphs, const Texture &tex); void markGlyphsToRender(const QVector<glyph_t> &glyphs); - void removeGlyph(glyph_t glyph); + inline void removeGlyph(glyph_t glyph); void updateTexture(GLuint oldTex, GLuint newTex, const QSize &newTexSize); - bool containsGlyph(glyph_t glyph) const; + inline bool containsGlyph(glyph_t glyph); GLuint textureIdForGlyph(glyph_t glyph) const; QOpenGLContext *ctx; private: - struct GlyphCacheData : public QOpenGLSharedResource { - QList<Texture> textures; - QHash<glyph_t, Texture*> glyphTextures; - QHash<glyph_t, TexCoord> texCoords; - QDataBuffer<glyph_t> pendingGlyphs; - QHash<glyph_t, QPainterPath> glyphPaths; - bool doubleGlyphResolution; - QLinkedList<QSGDistanceFieldGlyphNode*> m_registeredNodes; - QHash<glyph_t, quint32> glyphRefCount; - - GlyphCacheData(QOpenGLContext *ctx) - : QOpenGLSharedResource(ctx->shareGroup()) - , pendingGlyphs(64) - , doubleGlyphResolution(false) - {} - - void invalidateResource() - { - textures.clear(); - glyphTextures.clear(); - texCoords.clear(); - } - - void freeResource(QOpenGLContext *) - { - } + struct GlyphData { + Texture *texture; + TexCoord texCoord; + QRectF boundingRect; + quint32 ref; + + GlyphData() : texture(0), ref(0) { } }; + GlyphData &glyphData(glyph_t glyph); + QSGDistanceFieldGlyphCacheManager *m_manager; - QRawFont m_font; QRawFont m_referenceFont; - int m_glyphCount; - QHash<glyph_t, Metrics> m_metrics; - GlyphCacheData *cacheData(); - GlyphCacheData *m_cacheData; - static QHash<QString, QOpenGLMultiGroupSharedResource> m_caches_data; + bool m_doubleGlyphResolution; + + QList<Texture> m_textures; + QHash<glyph_t, GlyphData> m_glyphsData; + QDataBuffer<glyph_t> m_pendingGlyphs; + QLinkedList<QSGDistanceFieldGlyphConsumer*> m_registeredNodes; + + static Texture s_emptyTexture; }; +inline QSGDistanceFieldGlyphCache::TexCoord QSGDistanceFieldGlyphCache::glyphTexCoord(glyph_t glyph) +{ + return glyphData(glyph).texCoord; +} + +inline const QSGDistanceFieldGlyphCache::Texture *QSGDistanceFieldGlyphCache::glyphTexture(glyph_t glyph) +{ + return glyphData(glyph).texture; +} + +inline void QSGDistanceFieldGlyphCache::removeGlyph(glyph_t glyph) +{ + GlyphData &gd = glyphData(glyph); + gd.texCoord = TexCoord(); + gd.texture = &s_emptyTexture; +} + +inline bool QSGDistanceFieldGlyphCache::containsGlyph(glyph_t glyph) +{ + return glyphData(glyph).texCoord.isValid(); +} + QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index 02fbaa1a7f..da1c8e6ee9 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -54,6 +54,7 @@ #include <QGuiApplication> #include <QOpenGLContext> +#include <QtGui/qopenglframebufferobject.h> #include <private/qqmlglobal_p.h> @@ -95,7 +96,13 @@ class QSGContextPrivate : public QObjectPrivate public: QSGContextPrivate() : gl(0) + , depthStencilBufferManager(0) , distanceFieldCacheManager(0) + #ifndef QT_OPENGL_ES + , distanceFieldAntialiasing(QSGGlyphNode::HighQualitySubPixelAntialiasing) + #else + , distanceFieldAntialiasing(QSGGlyphNode::GrayAntialiasing) + #endif , flashMode(qmlFlashMode()) , distanceFieldDisabled(qmlDisableDistanceField()) { @@ -111,9 +118,11 @@ public: QHash<QSGMaterialType *, QSGMaterialShader *> materials; QMutex textureMutex; QHash<QQuickTextureFactory *, QSGTexture *> textures; - + QSGDepthStencilBufferManager *depthStencilBufferManager; QSGDistanceFieldGlyphCacheManager *distanceFieldCacheManager; + QSGDistanceFieldGlyphNode::AntialiasingMode distanceFieldAntialiasing; + bool flashMode; float renderAlpha; bool distanceFieldDisabled; @@ -141,6 +150,17 @@ public: QSGContext::QSGContext(QObject *parent) : QObject(*(new QSGContextPrivate), parent) { + Q_D(QSGContext); + // ### Do something with these before final release... + static bool doSubpixel = qApp->arguments().contains(QLatin1String("--text-subpixel-antialiasing")); + static bool doLowQualSubpixel = qApp->arguments().contains(QLatin1String("--text-subpixel-antialiasing-lowq")); + static bool doGray = qApp->arguments().contains(QLatin1String("--text-gray-antialiasing")); + if (doSubpixel) + d->distanceFieldAntialiasing = QSGGlyphNode::HighQualitySubPixelAntialiasing; + else if (doLowQualSubpixel) + d->distanceFieldAntialiasing = QSGGlyphNode::LowQualitySubPixelAntialiasing; + else if (doGray) + d->distanceFieldAntialiasing = QSGGlyphNode::GrayAntialiasing; } @@ -160,6 +180,8 @@ void QSGContext::invalidate() d->textureMutex.unlock(); qDeleteAll(d->materials.values()); d->materials.clear(); + delete d->depthStencilBufferManager; + d->depthStencilBufferManager = 0; delete d->distanceFieldCacheManager; d->distanceFieldCacheManager = 0; @@ -270,39 +292,47 @@ QSGImageNode *QSGContext::createImageNode() /*! Factory function for scene graph backends of the distance-field glyph cache. */ -QSGDistanceFieldGlyphCache *QSGContext::createDistanceFieldGlyphCache(const QRawFont &font) +QSGDistanceFieldGlyphCache *QSGContext::distanceFieldGlyphCache(const QRawFont &font) { Q_D(QSGContext); - QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration(); - if (platformIntegration != 0 - && platformIntegration->hasCapability(QPlatformIntegration::SharedGraphicsCache)) { - QFontEngine *fe = QRawFontPrivate::get(font)->fontEngine; - if (!fe->faceId().filename.isEmpty()) { - QByteArray keyName = fe->faceId().filename; - if (font.style() != QFont::StyleNormal) - keyName += QByteArray(" I"); - if (font.weight() != QFont::Normal) - keyName += " " + QByteArray::number(font.weight()); - keyName += QByteArray(" DF"); - QPlatformSharedGraphicsCache *sharedGraphicsCache = - platformIntegration->createPlatformSharedGraphicsCache(keyName); - - if (sharedGraphicsCache != 0) { - sharedGraphicsCache->ensureCacheInitialized(keyName, - QPlatformSharedGraphicsCache::OpenGLTexture, - QPlatformSharedGraphicsCache::Alpha8); - - return new QSGSharedDistanceFieldGlyphCache(keyName, - sharedGraphicsCache, - d->distanceFieldCacheManager, - glContext(), - font); + if (!d->distanceFieldCacheManager) + d->distanceFieldCacheManager = new QSGDistanceFieldGlyphCacheManager; + + QSGDistanceFieldGlyphCache *cache = d->distanceFieldCacheManager->cache(font); + if (!cache) { + QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration(); + if (platformIntegration != 0 + && platformIntegration->hasCapability(QPlatformIntegration::SharedGraphicsCache)) { + QFontEngine *fe = QRawFontPrivate::get(font)->fontEngine; + if (!fe->faceId().filename.isEmpty()) { + QByteArray keyName = fe->faceId().filename; + if (font.style() != QFont::StyleNormal) + keyName += QByteArray(" I"); + if (font.weight() != QFont::Normal) + keyName += " " + QByteArray::number(font.weight()); + keyName += QByteArray(" DF"); + QPlatformSharedGraphicsCache *sharedGraphicsCache = + platformIntegration->createPlatformSharedGraphicsCache(keyName); + + if (sharedGraphicsCache != 0) { + sharedGraphicsCache->ensureCacheInitialized(keyName, + QPlatformSharedGraphicsCache::OpenGLTexture, + QPlatformSharedGraphicsCache::Alpha8); + + cache = new QSGSharedDistanceFieldGlyphCache(keyName, + sharedGraphicsCache, + d->distanceFieldCacheManager, + glContext(), + font); + } } } + if (!cache) + cache = new QSGDefaultDistanceFieldGlyphCache(d->distanceFieldCacheManager, glContext(), font); + d->distanceFieldCacheManager->insertCache(font, cache); } - - return new QSGDefaultDistanceFieldGlyphCache(d->distanceFieldCacheManager, glContext(), font); + return cache; } /*! @@ -312,25 +342,11 @@ QSGGlyphNode *QSGContext::createGlyphNode() { Q_D(QSGContext); - // ### Do something with these before final release... - static bool doSubpixel = qApp->arguments().contains(QLatin1String("--text-subpixel-antialiasing")); - static bool doLowQualSubpixel = qApp->arguments().contains(QLatin1String("--text-subpixel-antialiasing-lowq")); - static bool doGray = qApp->arguments().contains(QLatin1String("--text-gray-antialiasing")); - if (d->distanceFieldDisabled) { return new QSGDefaultGlyphNode; } else { - if (!d->distanceFieldCacheManager) { - d->distanceFieldCacheManager = new QSGDistanceFieldGlyphCacheManager(this); - if (doSubpixel) - d->distanceFieldCacheManager->setDefaultAntialiasingMode(QSGGlyphNode::HighQualitySubPixelAntialiasing); - else if (doLowQualSubpixel) - d->distanceFieldCacheManager->setDefaultAntialiasingMode(QSGGlyphNode::LowQualitySubPixelAntialiasing); - else if (doGray) - d->distanceFieldCacheManager->setDefaultAntialiasingMode(QSGGlyphNode::GrayAntialiasing); - } - - QSGGlyphNode *node = new QSGDistanceFieldGlyphNode(d->distanceFieldCacheManager); + QSGDistanceFieldGlyphNode *node = new QSGDistanceFieldGlyphNode(this); + node->setPreferredAntialiasingMode(d->distanceFieldAntialiasing); return node; } } @@ -399,6 +415,42 @@ QSize QSGContext::minimumFBOSize() const /*! + Returns a shared pointer to a depth stencil buffer that can be used with \a fbo. + */ +QSharedPointer<QSGDepthStencilBuffer> QSGContext::depthStencilBufferForFbo(QOpenGLFramebufferObject *fbo) +{ + Q_D(QSGContext); + if (!d->gl) + return QSharedPointer<QSGDepthStencilBuffer>(); + QSGDepthStencilBufferManager *manager = depthStencilBufferManager(); + QSGDepthStencilBuffer::Format format; + format.size = fbo->size(); + format.samples = fbo->format().samples(); + format.attachments = QSGDepthStencilBuffer::DepthAttachment | QSGDepthStencilBuffer::StencilAttachment; + QSharedPointer<QSGDepthStencilBuffer> buffer = manager->bufferForFormat(format); + if (buffer.isNull()) { + buffer = QSharedPointer<QSGDepthStencilBuffer>(new QSGDefaultDepthStencilBuffer(d->gl, format)); + manager->insertBuffer(buffer); + } + return buffer; +} + +/*! + Returns a pointer to the context's depth/stencil buffer manager. This is useful for custom + implementations of \l depthStencilBufferForFbo(). + */ +QSGDepthStencilBufferManager *QSGContext::depthStencilBufferManager() +{ + Q_D(QSGContext); + if (!d->gl) + return 0; + if (!d->depthStencilBufferManager) + d->depthStencilBufferManager = new QSGDepthStencilBufferManager(d->gl); + return d->depthStencilBufferManager; +} + + +/*! Returns a material shader for the given material. */ diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h index dfb960f420..7cbff40a9b 100644 --- a/src/quick/scenegraph/qsgcontext_p.h +++ b/src/quick/scenegraph/qsgcontext_p.h @@ -51,6 +51,7 @@ #include <private/qrawfont_p.h> #include <QtQuick/qsgnode.h> +#include <QtQuick/private/qsgdepthstencilbuffer_p.h> QT_BEGIN_HEADER @@ -95,7 +96,7 @@ public: virtual void renderNextFrame(QSGRenderer *renderer, GLuint fboId); - virtual QSGDistanceFieldGlyphCache *createDistanceFieldGlyphCache(const QRawFont &font); + virtual QSGDistanceFieldGlyphCache *distanceFieldGlyphCache(const QRawFont &font); virtual QSGRectangleNode *createRectangleNode(); virtual QSGImageNode *createImageNode(); @@ -104,6 +105,8 @@ public: virtual QSGTexture *createTexture(const QImage &image = QImage()) const; virtual QSize minimumFBOSize() const; + virtual QSharedPointer<QSGDepthStencilBuffer> depthStencilBufferForFbo(QOpenGLFramebufferObject *fbo); + QSGDepthStencilBufferManager *depthStencilBufferManager(); virtual QSurfaceFormat defaultSurfaceFormat() const; diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp index 5864f35060..2b2ccfe317 100644 --- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp +++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp @@ -47,24 +47,41 @@ QT_BEGIN_NAMESPACE -QHash<QString, QOpenGLMultiGroupSharedResource> QSGDefaultDistanceFieldGlyphCache::m_textures_data; - -QSGDefaultDistanceFieldGlyphCache::DistanceFieldTextureData *QSGDefaultDistanceFieldGlyphCache::textureData(QOpenGLContext *c) -{ - QString key = QString::fromLatin1("%1_%2_%3_%4") - .arg(font().familyName()) - .arg(font().styleName()) - .arg(font().weight()) - .arg(font().style()); - return m_textures_data[key].value<QSGDefaultDistanceFieldGlyphCache::DistanceFieldTextureData>(c); -} QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font) : QSGDistanceFieldGlyphCache(man, c, font) , m_maxTextureSize(0) , m_maxTextureCount(3) + , m_fbo(0) + , m_blitProgram(0) +{ + m_currentTexture = createTextureInfo(); + + m_blitVertexCoordinateArray[0] = -1.0f; + m_blitVertexCoordinateArray[1] = -1.0f; + m_blitVertexCoordinateArray[2] = 1.0f; + m_blitVertexCoordinateArray[3] = -1.0f; + m_blitVertexCoordinateArray[4] = 1.0f; + m_blitVertexCoordinateArray[5] = 1.0f; + m_blitVertexCoordinateArray[6] = -1.0f; + m_blitVertexCoordinateArray[7] = 1.0f; + + m_blitTextureCoordinateArray[0] = 0.0f; + m_blitTextureCoordinateArray[1] = 0.0f; + m_blitTextureCoordinateArray[2] = 1.0f; + m_blitTextureCoordinateArray[3] = 0.0f; + m_blitTextureCoordinateArray[4] = 1.0f; + m_blitTextureCoordinateArray[5] = 1.0f; + m_blitTextureCoordinateArray[6] = 0.0f; + m_blitTextureCoordinateArray[7] = 1.0f; +} + +QSGDefaultDistanceFieldGlyphCache::~QSGDefaultDistanceFieldGlyphCache() { - m_textureData = textureData(c); + for (int i = 0; i < m_textures.count(); ++i) + glDeleteTextures(1, &m_textures[i].texture); + ctx->functions()->glDeleteFramebuffers(1, &m_fbo); + delete m_blitProgram; } void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs) @@ -75,15 +92,15 @@ void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyph for (QSet<glyph_t>::const_iterator it = glyphs.constBegin(); it != glyphs.constEnd() ; ++it) { glyph_t glyphIndex = *it; - if (cacheIsFull() && m_textureData->unusedGlyphs.isEmpty()) + if (cacheIsFull() && m_unusedGlyphs.isEmpty()) continue; - if (textureIsFull(m_textureData->currentTexture) && m_textureData->textures.count() < m_maxTextureCount) - m_textureData->currentTexture = m_textureData->addTexture(); + if (textureIsFull(m_currentTexture) && m_textures.count() < m_maxTextureCount) + m_currentTexture = createTextureInfo(); - m_textureData->unusedGlyphs.remove(glyphIndex); + m_unusedGlyphs.remove(glyphIndex); - DistanceFieldTextureData::TextureInfo *tex = m_textureData->currentTexture; + TextureInfo *tex = m_currentTexture; GlyphPosition p; p.glyph = glyphIndex; @@ -97,13 +114,13 @@ void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyph } } else { // Recycle glyphs - if (!m_textureData->unusedGlyphs.isEmpty()) { - glyph_t unusedGlyph = *m_textureData->unusedGlyphs.constBegin(); + if (!m_unusedGlyphs.isEmpty()) { + glyph_t unusedGlyph = *m_unusedGlyphs.constBegin(); TexCoord unusedCoord = glyphTexCoord(unusedGlyph); - tex = m_textureData->glyphsTexture.value(unusedGlyph); + tex = m_glyphsTexture.value(unusedGlyph); p.position = QPointF(unusedCoord.x, unusedCoord.y); - m_textureData->unusedGlyphs.remove(unusedGlyph); - m_textureData->glyphsTexture.remove(unusedGlyph); + m_unusedGlyphs.remove(unusedGlyph); + m_glyphsTexture.remove(unusedGlyph); removeGlyph(unusedGlyph); } } @@ -111,7 +128,7 @@ void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyph if (p.position.y() < maxTextureSize()) { glyphPositions.append(p); glyphsToRender.append(glyphIndex); - m_textureData->glyphsTexture.insert(glyphIndex, tex); + m_glyphsTexture.insert(glyphIndex, tex); } } @@ -124,13 +141,13 @@ void QSGDefaultDistanceFieldGlyphCache::storeGlyphs(const QHash<glyph_t, QImage> int requiredWidth = maxTextureSize(); int rows = 128 / (requiredWidth / QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution())); // Enough rows to fill the latin1 set by default.. - QHash<DistanceFieldTextureData::TextureInfo *, QVector<glyph_t> > glyphTextures; + QHash<TextureInfo *, QVector<glyph_t> > glyphTextures; QHash<glyph_t, QImage>::const_iterator it; for (it = glyphs.constBegin(); it != glyphs.constEnd(); ++it) { glyph_t glyphIndex = it.key(); TexCoord c = glyphTexCoord(glyphIndex); - DistanceFieldTextureData::TextureInfo *texInfo = m_textureData->glyphsTexture.value(glyphIndex); + TextureInfo *texInfo = m_glyphsTexture.value(glyphIndex); int requiredHeight = qMin(maxTextureSize(), qMax(texInfo->currY + QT_DISTANCEFIELD_TILESIZE(doubleGlyphResolution()), @@ -156,7 +173,7 @@ void QSGDefaultDistanceFieldGlyphCache::storeGlyphs(const QHash<glyph_t, QImage> glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, glyph.width(), glyph.height(), GL_ALPHA, GL_UNSIGNED_BYTE, glyph.constBits()); } - QHash<DistanceFieldTextureData::TextureInfo *, QVector<glyph_t> >::const_iterator i; + QHash<TextureInfo *, QVector<glyph_t> >::const_iterator i; for (i = glyphTextures.constBegin(); i != glyphTextures.constEnd(); ++i) { Texture t; t.textureId = i.key()->texture; @@ -167,15 +184,15 @@ void QSGDefaultDistanceFieldGlyphCache::storeGlyphs(const QHash<glyph_t, QImage> void QSGDefaultDistanceFieldGlyphCache::referenceGlyphs(const QSet<glyph_t> &glyphs) { - m_textureData->unusedGlyphs -= glyphs; + m_unusedGlyphs -= glyphs; } void QSGDefaultDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyphs) { - m_textureData->unusedGlyphs += glyphs; + m_unusedGlyphs += glyphs; } -void QSGDefaultDistanceFieldGlyphCache::createTexture(DistanceFieldTextureData::TextureInfo *texInfo, int width, int height) +void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo, int width, int height) { if (useWorkaroundBrokenFBOReadback() && texInfo->image.isNull()) texInfo->image = QImage(width, height, QImage::Format_Indexed8); @@ -202,7 +219,7 @@ void QSGDefaultDistanceFieldGlyphCache::createTexture(DistanceFieldTextureData:: } -void QSGDefaultDistanceFieldGlyphCache::resizeTexture(DistanceFieldTextureData::TextureInfo *texInfo, int width, int height) +void QSGDefaultDistanceFieldGlyphCache::resizeTexture(TextureInfo *texInfo, int width, int height) { int oldWidth = texInfo->size.width(); int oldHeight = texInfo->size.height(); @@ -224,14 +241,14 @@ void QSGDefaultDistanceFieldGlyphCache::resizeTexture(DistanceFieldTextureData:: return; } - if (!m_textureData->blitProgram) - m_textureData->createBlitProgram(); + if (!m_blitProgram) + createBlitProgram(); - Q_ASSERT(m_textureData->blitProgram); + Q_ASSERT(m_blitProgram); - if (!m_textureData->fbo) - ctx->functions()->glGenFramebuffers(1, &m_textureData->fbo); - ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, m_textureData->fbo); + if (!m_fbo) + ctx->functions()->glGenFramebuffers(1, &m_fbo); + ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); GLuint tmp_texture; glGenTextures(1, &tmp_texture); @@ -270,14 +287,14 @@ void QSGDefaultDistanceFieldGlyphCache::resizeTexture(DistanceFieldTextureData:: glViewport(0, 0, oldWidth, oldHeight); - ctx->functions()->glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_textureData->blitVertexCoordinateArray); - ctx->functions()->glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_textureData->blitTextureCoordinateArray); + ctx->functions()->glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_blitVertexCoordinateArray); + ctx->functions()->glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, m_blitTextureCoordinateArray); - m_textureData->blitProgram->bind(); - m_textureData->blitProgram->enableAttributeArray(int(QT_VERTEX_COORDS_ATTR)); - m_textureData->blitProgram->enableAttributeArray(int(QT_TEXTURE_COORDS_ATTR)); - m_textureData->blitProgram->disableAttributeArray(int(QT_OPACITY_ATTR)); - m_textureData->blitProgram->setUniformValue("imageTexture", GLuint(0)); + m_blitProgram->bind(); + m_blitProgram->enableAttributeArray(int(QT_VERTEX_COORDS_ATTR)); + m_blitProgram->enableAttributeArray(int(QT_TEXTURE_COORDS_ATTR)); + m_blitProgram->disableAttributeArray(int(QT_OPACITY_ATTR)); + m_blitProgram->setUniformValue("imageTexture", GLuint(0)); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); @@ -304,8 +321,8 @@ void QSGDefaultDistanceFieldGlyphCache::resizeTexture(DistanceFieldTextureData:: glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); ctx->functions()->glUseProgram(oldProgram); - m_textureData->blitProgram->disableAttributeArray(int(QT_VERTEX_COORDS_ATTR)); - m_textureData->blitProgram->disableAttributeArray(int(QT_TEXTURE_COORDS_ATTR)); + m_blitProgram->disableAttributeArray(int(QT_VERTEX_COORDS_ATTR)); + m_blitProgram->disableAttributeArray(int(QT_TEXTURE_COORDS_ATTR)); } bool QSGDefaultDistanceFieldGlyphCache::useWorkaroundBrokenFBOReadback() const diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h index 12bbcce060..38cc649b3a 100644 --- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h +++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h @@ -53,6 +53,7 @@ class Q_QUICK_EXPORT QSGDefaultDistanceFieldGlyphCache : public QSGDistanceField { public: QSGDefaultDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font); + virtual ~QSGDefaultDistanceFieldGlyphCache(); void requestGlyphs(const QSet<glyph_t> &glyphs); void storeGlyphs(const QHash<glyph_t, QImage> &glyphs); @@ -60,8 +61,8 @@ public: void releaseGlyphs(const QSet<glyph_t> &glyphs); bool cacheIsFull() const { - return m_textureData->textures.count() == m_maxTextureCount - && textureIsFull(m_textureData->currentTexture); + return m_textures.count() == m_maxTextureCount + && textureIsFull(m_currentTexture); } bool useWorkaroundBrokenFBOReadback() const; int maxTextureSize() const; @@ -70,123 +71,67 @@ public: int maxTextureCount() const { return m_maxTextureCount; } private: - mutable int m_maxTextureSize; - int m_maxTextureCount; + struct TextureInfo { + GLuint texture; + QSize size; + int currX; + int currY; + QImage image; + + TextureInfo() : texture(0), currX(0), currY(0) + { } + }; - struct DistanceFieldTextureData : public QOpenGLSharedResource { - struct TextureInfo { - GLuint texture; - QSize size; - int currX; - int currY; - QImage image; - - TextureInfo() : texture(0), currX(0), currY(0) - { } - }; - - TextureInfo *currentTexture; - QList<TextureInfo> textures; - QHash<glyph_t, TextureInfo *> glyphsTexture; - GLuint fbo; - QSet<glyph_t> unusedGlyphs; - - QOpenGLShaderProgram *blitProgram; - GLfloat blitVertexCoordinateArray[8]; - GLfloat blitTextureCoordinateArray[8]; - - TextureInfo *addTexture() - { - textures.append(TextureInfo()); - return &textures.last(); - } + void createTexture(TextureInfo * texInfo, int width, int height); + void resizeTexture(TextureInfo * texInfo, int width, int height); + bool textureIsFull (const TextureInfo *tex) const { return tex->currY >= maxTextureSize(); } - DistanceFieldTextureData(QOpenGLContext *ctx) - : QOpenGLSharedResource(ctx->shareGroup()) - , fbo(0) - , blitProgram(0) - { - currentTexture = addTexture(); - - blitVertexCoordinateArray[0] = -1.0f; - blitVertexCoordinateArray[1] = -1.0f; - blitVertexCoordinateArray[2] = 1.0f; - blitVertexCoordinateArray[3] = -1.0f; - blitVertexCoordinateArray[4] = 1.0f; - blitVertexCoordinateArray[5] = 1.0f; - blitVertexCoordinateArray[6] = -1.0f; - blitVertexCoordinateArray[7] = 1.0f; - - blitTextureCoordinateArray[0] = 0.0f; - blitTextureCoordinateArray[1] = 0.0f; - blitTextureCoordinateArray[2] = 1.0f; - blitTextureCoordinateArray[3] = 0.0f; - blitTextureCoordinateArray[4] = 1.0f; - blitTextureCoordinateArray[5] = 1.0f; - blitTextureCoordinateArray[6] = 0.0f; - blitTextureCoordinateArray[7] = 1.0f; - } + TextureInfo *createTextureInfo() + { + m_textures.append(TextureInfo()); + return &m_textures.last(); + } - void invalidateResource() + void createBlitProgram() + { + m_blitProgram = new QOpenGLShaderProgram; { - glyphsTexture.clear(); - textures.clear(); - fbo = 0; - delete blitProgram; - blitProgram = 0; + QString source; + source.append(QLatin1String(qopenglslMainWithTexCoordsVertexShader)); + source.append(QLatin1String(qopenglslUntransformedPositionVertexShader)); - currentTexture = addTexture(); - } + QOpenGLShader *vertexShader = new QOpenGLShader(QOpenGLShader::Vertex, m_blitProgram); + vertexShader->compileSourceCode(source); - void freeResource(QOpenGLContext *ctx) - { - glyphsTexture.clear(); - for (int i = 0; i < textures.count(); ++i) - glDeleteTextures(1, &textures[i].texture); - textures.clear(); - ctx->functions()->glDeleteFramebuffers(1, &fbo); - delete blitProgram; - blitProgram = 0; - - currentTexture = addTexture(); + m_blitProgram->addShader(vertexShader); } - - void createBlitProgram() { - blitProgram = new QOpenGLShaderProgram; - { - QString source; - source.append(QLatin1String(qopenglslMainWithTexCoordsVertexShader)); - source.append(QLatin1String(qopenglslUntransformedPositionVertexShader)); - - QOpenGLShader *vertexShader = new QOpenGLShader(QOpenGLShader::Vertex, blitProgram); - vertexShader->compileSourceCode(source); - - blitProgram->addShader(vertexShader); - } - { - QString source; - source.append(QLatin1String(qopenglslMainFragmentShader)); - source.append(QLatin1String(qopenglslImageSrcFragmentShader)); - - QOpenGLShader *fragmentShader = new QOpenGLShader(QOpenGLShader::Fragment, blitProgram); - fragmentShader->compileSourceCode(source); - - blitProgram->addShader(fragmentShader); - } - blitProgram->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); - blitProgram->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); - blitProgram->link(); + QString source; + source.append(QLatin1String(qopenglslMainFragmentShader)); + source.append(QLatin1String(qopenglslImageSrcFragmentShader)); + + QOpenGLShader *fragmentShader = new QOpenGLShader(QOpenGLShader::Fragment, m_blitProgram); + fragmentShader->compileSourceCode(source); + + m_blitProgram->addShader(fragmentShader); } - }; + m_blitProgram->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); + m_blitProgram->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); + m_blitProgram->link(); + } + + mutable int m_maxTextureSize; + int m_maxTextureCount; - void createTexture(DistanceFieldTextureData::TextureInfo * texInfo, int width, int height); - void resizeTexture(DistanceFieldTextureData::TextureInfo * texInfo, int width, int height); - bool textureIsFull (const DistanceFieldTextureData::TextureInfo *tex) const { return tex->currY >= maxTextureSize(); } + TextureInfo *m_currentTexture; + QList<TextureInfo> m_textures; + QHash<glyph_t, TextureInfo *> m_glyphsTexture; + GLuint m_fbo; + QSet<glyph_t> m_unusedGlyphs; - DistanceFieldTextureData *textureData(QOpenGLContext *c); - DistanceFieldTextureData *m_textureData; - static QHash<QString, QOpenGLMultiGroupSharedResource> m_textures_data; + QOpenGLShaderProgram *m_blitProgram; + GLfloat m_blitVertexCoordinateArray[8]; + GLfloat m_blitTextureCoordinateArray[8]; }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp index a86e663755..d6fe6f938c 100644 --- a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp @@ -46,9 +46,10 @@ QT_BEGIN_NAMESPACE -QSGDistanceFieldGlyphNode::QSGDistanceFieldGlyphNode(QSGDistanceFieldGlyphCacheManager *cacheManager) - : m_material(0) - , m_glyph_cacheManager(cacheManager) +QSGDistanceFieldGlyphNode::QSGDistanceFieldGlyphNode(QSGContext *context) + : m_glyphNodeType(RootGlyphNode) + , m_context(context) + , m_material(0) , m_glyph_cache(0) , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0) , m_style(QQuickText::Normal) @@ -59,7 +60,6 @@ QSGDistanceFieldGlyphNode::QSGDistanceFieldGlyphNode(QSGDistanceFieldGlyphCacheM { m_geometry.setDrawingMode(GL_TRIANGLES); setGeometry(&m_geometry); - setPreferredAntialiasingMode(cacheManager->defaultAntialiasingMode()); setFlag(UsePreprocess); #ifdef QML_RUNTIME_TESTING description = QLatin1String("glyphs"); @@ -70,15 +70,17 @@ QSGDistanceFieldGlyphNode::~QSGDistanceFieldGlyphNode() { delete m_material; + if (m_glyphNodeType == SubGlyphNode) + return; + if (m_glyph_cache) { m_glyph_cache->release(m_glyphs.glyphIndexes()); m_glyph_cache->unregisterGlyphNode(this); m_glyph_cache->unregisterOwnerElement(ownerElement()); } - for (int i = 0; i < m_nodesToDelete.count(); ++i) - delete m_nodesToDelete.at(i); - m_nodesToDelete.clear(); + while (m_nodesToDelete.count()) + delete m_nodesToDelete.takeLast(); } void QSGDistanceFieldGlyphNode::setColor(const QColor &color) @@ -107,8 +109,15 @@ void QSGDistanceFieldGlyphNode::setGlyphs(const QPointF &position, const QGlyphR m_position = QPointF(position.x(), position.y() - font.ascent()); m_glyphs = glyphs; + m_dirtyGeometry = true; + m_dirtyMaterial = true; + QSGDistanceFieldGlyphCache *oldCache = m_glyph_cache; - m_glyph_cache = m_glyph_cacheManager->cache(m_glyphs.rawFont()); + m_glyph_cache = m_context->distanceFieldGlyphCache(m_glyphs.rawFont()); + + if (m_glyphNodeType == SubGlyphNode) + return; + if (m_glyph_cache != oldCache) { Q_ASSERT(ownerElement() != 0); if (oldCache) { @@ -123,9 +132,6 @@ void QSGDistanceFieldGlyphNode::setGlyphs(const QPointF &position, const QGlyphR const QVector<quint32> glyphIndexes = m_glyphs.glyphIndexes(); for (int i = 0; i < glyphIndexes.count(); ++i) m_allGlyphIndexesLookup.insert(glyphIndexes.at(i)); - - m_dirtyGeometry = true; - m_dirtyMaterial = true; } void QSGDistanceFieldGlyphNode::setStyle(QQuickText::TextStyle style) @@ -154,9 +160,8 @@ void QSGDistanceFieldGlyphNode::preprocess() { Q_ASSERT(m_glyph_cache); - for (int i = 0; i < m_nodesToDelete.count(); ++i) - delete m_nodesToDelete.at(i); - m_nodesToDelete.clear(); + while (m_nodesToDelete.count()) + delete m_nodesToDelete.takeLast(); m_glyph_cache->processPendingGlyphs(); m_glyph_cache->update(); @@ -183,14 +188,15 @@ void QSGDistanceFieldGlyphNode::updateGeometry() Q_ASSERT(m_glyph_cache); // Remove previously created sub glyph nodes - QHash<const QSGDistanceFieldGlyphCache::Texture *, QSGDistanceFieldGlyphNode *>::iterator it = m_subNodes.begin(); - while (it != m_subNodes.end()) { - removeChildNode(it.value()); + // We assume all the children are sub glyph nodes + QSGNode *subnode = firstChild(); + while (subnode) { // We can't delete the node now as it might be in the preprocess list // It will be deleted in the next preprocess - m_nodesToDelete.append(it.value()); - it = m_subNodes.erase(it); + m_nodesToDelete.append(subnode); + subnode = subnode->nextSibling(); } + removeAllChildNodes(); QSGGeometry *g = geometry(); @@ -200,6 +206,7 @@ void QSGDistanceFieldGlyphNode::updateGeometry() const QVector<quint32> indexes = m_glyphs.glyphIndexes(); const QVector<QPointF> positions = m_glyphs.positions(); + qreal fontPixelSize = m_glyphs.rawFont().pixelSize(); QVector<QSGGeometry::TexturedPoint2D> vp; vp.reserve(indexes.size() * 4); @@ -207,8 +214,7 @@ void QSGDistanceFieldGlyphNode::updateGeometry() ip.reserve(indexes.size() * 6); QPointF margins(2, 2); - QPointF texMargins = margins / m_glyph_cache->fontScale(); - + QPointF texMargins = margins / m_glyph_cache->fontScale(fontPixelSize); for (int i = 0; i < indexes.size(); ++i) { const int glyphIndex = indexes.at(i); @@ -232,7 +238,7 @@ void QSGDistanceFieldGlyphNode::updateGeometry() continue; } - QSGDistanceFieldGlyphCache::Metrics metrics = m_glyph_cache->glyphMetrics(glyphIndex); + QSGDistanceFieldGlyphCache::Metrics metrics = m_glyph_cache->glyphMetrics(glyphIndex, fontPixelSize); if (!metrics.isNull() && !c.isNull()) { metrics.width += margins.x() * 2; @@ -288,25 +294,19 @@ void QSGDistanceFieldGlyphNode::updateGeometry() QHash<const QSGDistanceFieldGlyphCache::Texture *, GlyphInfo>::const_iterator ite = glyphsInOtherTextures.constBegin(); while (ite != glyphsInOtherTextures.constEnd()) { - QHash<const QSGDistanceFieldGlyphCache::Texture *, QSGDistanceFieldGlyphNode *>::iterator subIt = m_subNodes.find(ite.key()); - if (subIt == m_subNodes.end()) { - QSGDistanceFieldGlyphNode *subNode = new QSGDistanceFieldGlyphNode(m_glyph_cacheManager); - subNode->setOwnerElement(m_ownerElement); - subNode->setColor(m_color); - subNode->setStyle(m_style); - subNode->setStyleColor(m_styleColor); - subNode->update(); - appendChildNode(subNode); - subIt = m_subNodes.insert(ite.key(), subNode); - } - QGlyphRun subNodeGlyphRun(m_glyphs); subNodeGlyphRun.setGlyphIndexes(ite->indexes); subNodeGlyphRun.setPositions(ite->positions); - subIt.value()->setGlyphs(m_originalPosition, subNodeGlyphRun); - subIt.value()->update(); - subIt.value()->updateGeometry(); // we have to explicity call this now as preprocess won't be called before it's rendered + QSGDistanceFieldGlyphNode *subNode = new QSGDistanceFieldGlyphNode(m_context); + subNode->setGlyphNodeType(SubGlyphNode); + subNode->setColor(m_color); + subNode->setStyle(m_style); + subNode->setStyleColor(m_styleColor); + subNode->setGlyphs(m_originalPosition, subNodeGlyphRun); + subNode->update(); + subNode->updateGeometry(); // we have to explicity call this now as preprocess won't be called before it's rendered + appendChildNode(subNode); ++ite; } @@ -356,6 +356,8 @@ void QSGDistanceFieldGlyphNode::updateMaterial() } m_material->setGlyphCache(m_glyph_cache); + if (m_glyph_cache) + m_material->setFontScale(m_glyph_cache->fontScale(m_glyphs.rawFont().pixelSize())); m_material->setColor(m_color); setMaterial(m_material); m_dirtyMaterial = false; diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp index c66b82c16e..cd988c70d7 100644 --- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp @@ -151,8 +151,8 @@ void QSGDistanceFieldTextMaterialShader::updateState(const RenderState &state, Q bool updateRange = false; if (oldMaterial == 0 - || material->glyphCache()->fontScale() != oldMaterial->glyphCache()->fontScale()) { - m_fontScale = material->glyphCache()->fontScale(); + || material->fontScale() != oldMaterial->fontScale()) { + m_fontScale = material->fontScale(); updateRange = true; } if (state.isMatrixDirty()) { @@ -226,10 +226,8 @@ int QSGDistanceFieldTextMaterial::compare(const QSGMaterial *o) const const QSGDistanceFieldTextMaterial *other = static_cast<const QSGDistanceFieldTextMaterial *>(o); if (m_glyph_cache != other->m_glyph_cache) return m_glyph_cache - other->m_glyph_cache; - if (m_glyph_cache->fontScale() != other->m_glyph_cache->fontScale()) { - qreal s1 = m_glyph_cache->fontScale(); - qreal s2 = other->m_glyph_cache->fontScale(); - return int(s2 < s1) - int(s1 < s2); + if (m_fontScale != other->m_fontScale) { + return int(other->m_fontScale < m_fontScale) - int(m_fontScale < other->m_fontScale); } QRgb c1 = m_color.rgba(); QRgb c2 = other->m_color.rgba(); @@ -371,7 +369,7 @@ void DistanceFieldOutlineTextMaterialShader::updateState(const RenderState &stat QSGDistanceFieldOutlineTextMaterial *oldMaterial = static_cast<QSGDistanceFieldOutlineTextMaterial *>(oldEffect); if (oldMaterial == 0 - || material->glyphCache()->fontScale() != oldMaterial->glyphCache()->fontScale() + || material->fontScale() != oldMaterial->fontScale() || state.isMatrixDirty()) updateOutlineAlphaRange(material->glyphCache()->distanceFieldRadius()); } @@ -410,7 +408,7 @@ protected: virtual const char *vertexShader() const; virtual const char *fragmentShader() const; - void updateShift(const QSGDistanceFieldGlyphCache *cache, const QPointF& shift); + void updateShift(qreal fontScale, const QPointF& shift); int m_shift_id; }; @@ -434,17 +432,17 @@ void DistanceFieldShiftedStyleTextMaterialShader::updateState(const RenderState QSGDistanceFieldShiftedStyleTextMaterial *oldMaterial = static_cast<QSGDistanceFieldShiftedStyleTextMaterial *>(oldEffect); if (oldMaterial == 0 - || oldMaterial->glyphCache()->fontScale() != material->glyphCache()->fontScale() + || oldMaterial->fontScale() != material->fontScale() || oldMaterial->shift() != material->shift() || oldMaterial->textureSize() != material->textureSize()) { - updateShift(material->glyphCache(), material->shift()); + updateShift(material->fontScale(), material->shift()); } } -void DistanceFieldShiftedStyleTextMaterialShader::updateShift(const QSGDistanceFieldGlyphCache *cache, const QPointF &shift) +void DistanceFieldShiftedStyleTextMaterialShader::updateShift(qreal fontScale, const QPointF &shift) { - QPointF texel(1.0 / cache->fontScale() * shift.x(), - 1.0 / cache->fontScale() * shift.y()); + QPointF texel(1.0 / fontScale * shift.x(), + 1.0 / fontScale * shift.y()); program()->setUniformValue(m_shift_id, texel); } @@ -639,8 +637,8 @@ void QSGHiQSubPixelDistanceFieldTextMaterialShader::updateState(const RenderStat state.context()->functions()->glBlendColor(c.redF(), c.greenF(), c.blueF(), 1.0f); } - if (oldMaterial == 0 || material->glyphCache()->fontScale() != oldMaterial->glyphCache()->fontScale()) - program()->setUniformValue(m_fontScale_id, GLfloat(material->glyphCache()->fontScale())); + if (oldMaterial == 0 || material->fontScale() != oldMaterial->fontScale()) + program()->setUniformValue(m_fontScale_id, GLfloat(material->fontScale())); if (oldMaterial == 0 || state.isMatrixDirty()) { int viewportWidth = state.viewportRect().width(); diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h index 9406ee1bc8..146267c45d 100644 --- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h @@ -51,12 +51,13 @@ QT_BEGIN_HEADER QT_BEGIN_NAMESPACE +class QSGContext; class QSGDistanceFieldGlyphCacheManager; class QSGDistanceFieldTextMaterial; -class QSGDistanceFieldGlyphNode: public QSGGlyphNode +class QSGDistanceFieldGlyphNode: public QSGGlyphNode, public QSGDistanceFieldGlyphConsumer { public: - QSGDistanceFieldGlyphNode(QSGDistanceFieldGlyphCacheManager *cacheManager); + QSGDistanceFieldGlyphNode(QSGContext *context); ~QSGDistanceFieldGlyphNode(); virtual QPointF baseLine() const { return m_baseLine; } @@ -76,15 +77,22 @@ public: void updateGeometry(); private: + enum DistanceFieldGlyphNodeType { + RootGlyphNode, + SubGlyphNode + }; + + void setGlyphNodeType(DistanceFieldGlyphNodeType type) { m_glyphNodeType = type; } void updateMaterial(); + DistanceFieldGlyphNodeType m_glyphNodeType; QColor m_color; QPointF m_baseLine; + QSGContext *m_context; QSGDistanceFieldTextMaterial *m_material; QPointF m_originalPosition; QPointF m_position; QGlyphRun m_glyphs; - QSGDistanceFieldGlyphCacheManager *m_glyph_cacheManager; QSGDistanceFieldGlyphCache *m_glyph_cache; QSGGeometry m_geometry; QQuickText::TextStyle m_style; @@ -92,8 +100,7 @@ private: AntialiasingMode m_antialiasingMode; QRectF m_boundingRect; const QSGDistanceFieldGlyphCache::Texture *m_texture; - QHash<const QSGDistanceFieldGlyphCache::Texture *, QSGDistanceFieldGlyphNode *> m_subNodes; - QList<QSGDistanceFieldGlyphNode *> m_nodesToDelete; + QLinkedList<QSGNode *> m_nodesToDelete; struct GlyphInfo { QVector<quint32> indexes; diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h b/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h index c4af15a2af..8b2654e078 100644 --- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h @@ -48,7 +48,7 @@ QT_BEGIN_NAMESPACE -class QSGDistanceFieldTextMaterial: public QSGMaterial +class Q_QUICK_EXPORT QSGDistanceFieldTextMaterial: public QSGMaterial { public: QSGDistanceFieldTextMaterial(); @@ -67,6 +67,9 @@ public: void setTexture(const QSGDistanceFieldGlyphCache::Texture * tex) { m_texture = tex; } const QSGDistanceFieldGlyphCache::Texture * texture() const { return m_texture; } + void setFontScale(qreal fontScale) { m_fontScale = fontScale; } + qreal fontScale() const { return m_fontScale; } + QSize textureSize() const { return m_size; } bool updateTextureSize(); @@ -76,9 +79,10 @@ protected: QColor m_color; QSGDistanceFieldGlyphCache *m_glyph_cache; const QSGDistanceFieldGlyphCache::Texture *m_texture; + qreal m_fontScale; }; -class QSGDistanceFieldStyledTextMaterial : public QSGDistanceFieldTextMaterial +class Q_QUICK_EXPORT QSGDistanceFieldStyledTextMaterial : public QSGDistanceFieldTextMaterial { public: QSGDistanceFieldStyledTextMaterial(); @@ -95,7 +99,7 @@ protected: QColor m_styleColor; }; -class QSGDistanceFieldOutlineTextMaterial : public QSGDistanceFieldStyledTextMaterial +class Q_QUICK_EXPORT QSGDistanceFieldOutlineTextMaterial : public QSGDistanceFieldStyledTextMaterial { public: QSGDistanceFieldOutlineTextMaterial(); @@ -105,7 +109,7 @@ public: virtual QSGMaterialShader *createShader() const; }; -class QSGDistanceFieldShiftedStyleTextMaterial : public QSGDistanceFieldStyledTextMaterial +class Q_QUICK_EXPORT QSGDistanceFieldShiftedStyleTextMaterial : public QSGDistanceFieldStyledTextMaterial { public: QSGDistanceFieldShiftedStyleTextMaterial(); @@ -121,14 +125,14 @@ protected: QPointF m_shift; }; -class QSGHiQSubPixelDistanceFieldTextMaterial : public QSGDistanceFieldTextMaterial +class Q_QUICK_EXPORT QSGHiQSubPixelDistanceFieldTextMaterial : public QSGDistanceFieldTextMaterial { public: virtual QSGMaterialType *type() const; virtual QSGMaterialShader *createShader() const; }; -class QSGLoQSubPixelDistanceFieldTextMaterial : public QSGDistanceFieldTextMaterial +class Q_QUICK_EXPORT QSGLoQSubPixelDistanceFieldTextMaterial : public QSGDistanceFieldTextMaterial { public: virtual QSGMaterialType *type() const; diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri index f5fa18f87a..b6f7a228d2 100644 --- a/src/quick/scenegraph/scenegraph.pri +++ b/src/quick/scenegraph/scenegraph.pri @@ -23,6 +23,7 @@ SOURCES += \ # Util API HEADERS += \ $$PWD/util/qsgareaallocator_p.h \ + $$PWD/util/qsgdepthstencilbuffer_p.h \ $$PWD/util/qsgengine.h \ $$PWD/util/qsgflatcolormaterial.h \ $$PWD/util/qsgsimplematerial.h \ @@ -39,6 +40,7 @@ HEADERS += \ SOURCES += \ $$PWD/util/qsgareaallocator.cpp \ + $$PWD/util/qsgdepthstencilbuffer.cpp \ $$PWD/util/qsgengine.cpp \ $$PWD/util/qsgflatcolormaterial.cpp \ $$PWD/util/qsgsimplerectnode.cpp \ diff --git a/src/quick/scenegraph/util/qsgdepthstencilbuffer.cpp b/src/quick/scenegraph/util/qsgdepthstencilbuffer.cpp new file mode 100644 index 0000000000..bca57a3b6b --- /dev/null +++ b/src/quick/scenegraph/util/qsgdepthstencilbuffer.cpp @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgdepthstencilbuffer_p.h" + +QT_BEGIN_NAMESPACE + +QSGDepthStencilBuffer::QSGDepthStencilBuffer(QOpenGLContext *context, const Format &format) + : m_functions(context) + , m_manager(0) + , m_format(format) + , m_depthBuffer(0) + , m_stencilBuffer(0) +{ + // 'm_manager' is set by QSGDepthStencilBufferManager::insertBuffer(). +} + +QSGDepthStencilBuffer::~QSGDepthStencilBuffer() +{ + if (m_manager) + m_manager->m_buffers.remove(m_format); +} + +void QSGDepthStencilBuffer::attach() +{ + m_functions.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, m_depthBuffer); + m_functions.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, m_stencilBuffer); +} + +void QSGDepthStencilBuffer::detach() +{ + m_functions.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, 0); + m_functions.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, 0); +} + + +QSGDefaultDepthStencilBuffer::QSGDefaultDepthStencilBuffer(QOpenGLContext *context, const Format &format) + : QSGDepthStencilBuffer(context, format) +{ + const GLsizei width = format.size.width(); + const GLsizei height = format.size.height(); + + if (format.attachments == (DepthAttachment | StencilAttachment) + && m_functions.hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil)) + { + m_functions.glGenRenderbuffers(1, &m_depthBuffer); + m_functions.glBindRenderbuffer(GL_RENDERBUFFER, m_depthBuffer); + if (format.samples && m_functions.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) { + m_functions.glRenderbufferStorageMultisample(GL_RENDERBUFFER, format.samples, + GL_DEPTH24_STENCIL8, width, height); + } else { + m_functions.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); + } + m_stencilBuffer = m_depthBuffer; + } + if (!m_depthBuffer && (format.attachments & DepthAttachment)) { + m_functions.glGenRenderbuffers(1, &m_depthBuffer); + m_functions.glBindRenderbuffer(GL_RENDERBUFFER, m_depthBuffer); +#ifdef QT_OPENGL_ES + const GLenum internalFormat = m_functions.hasOpenGLExtension(QOpenGLExtensions::Depth24) + ? GL_DEPTH_COMPONENT24 : GL_DEPTH_COMPONENT16; +#else + const GLenum internalFormat = GL_DEPTH_COMPONENT; +#endif + if (format.samples && m_functions.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) { + m_functions.glRenderbufferStorageMultisample(GL_RENDERBUFFER, format.samples, + internalFormat, width, height); + } else { + m_functions.glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, width, height); + } + } + if (!m_stencilBuffer && (format.attachments & StencilAttachment)) { + m_functions.glGenRenderbuffers(1, &m_stencilBuffer); + m_functions.glBindRenderbuffer(GL_RENDERBUFFER, m_stencilBuffer); +#ifdef QT_OPENGL_ES + const GLenum internalFormat = GL_STENCIL_INDEX8; +#else + const GLenum internalFormat = GL_STENCIL_INDEX; +#endif + if (format.samples && m_functions.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) { + m_functions.glRenderbufferStorageMultisample(GL_RENDERBUFFER, format.samples, + internalFormat, width, height); + } else { + m_functions.glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, width, height); + } + } +} + +QSGDefaultDepthStencilBuffer::~QSGDefaultDepthStencilBuffer() +{ + free(); +} + +void QSGDefaultDepthStencilBuffer::free() +{ + if (m_depthBuffer) + m_functions.glDeleteRenderbuffers(1, &m_depthBuffer); + if (m_stencilBuffer && m_stencilBuffer != m_depthBuffer) + m_functions.glDeleteRenderbuffers(1, &m_stencilBuffer); + m_depthBuffer = m_stencilBuffer = 0; +} + + +QSGDepthStencilBufferManager::~QSGDepthStencilBufferManager() +{ + for (Hash::const_iterator it = m_buffers.constBegin(); it != m_buffers.constEnd(); ++it) { + QSGDepthStencilBuffer *buffer = it.value().data(); + buffer->free(); + buffer->m_manager = 0; + } +} + +QSharedPointer<QSGDepthStencilBuffer> QSGDepthStencilBufferManager::bufferForFormat(const QSGDepthStencilBuffer::Format &fmt) +{ + Hash::const_iterator it = m_buffers.constFind(fmt); + if (it != m_buffers.constEnd()) + return it.value().toStrongRef(); + return QSharedPointer<QSGDepthStencilBuffer>(); +} + +void QSGDepthStencilBufferManager::insertBuffer(const QSharedPointer<QSGDepthStencilBuffer> &buffer) +{ + Q_ASSERT(buffer->m_manager == 0); + Q_ASSERT(!m_buffers.contains(buffer->m_format)); + buffer->m_manager = this; + m_buffers.insert(buffer->m_format, buffer.toWeakRef()); +} + +uint qHash(const QSGDepthStencilBuffer::Format &format) +{ + return qHash(qMakePair(format.size.width(), format.size.height())) + ^ (uint(format.samples) << 12) ^ (uint(format.attachments) << 28); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgdepthstencilbuffer_p.h b/src/quick/scenegraph/util/qsgdepthstencilbuffer_p.h new file mode 100644 index 0000000000..0d8847feb4 --- /dev/null +++ b/src/quick/scenegraph/util/qsgdepthstencilbuffer_p.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGDEPTHSTENCILBUFFER_P_H +#define QSGDEPTHSTENCILBUFFER_P_H + +#include <QtCore/qsize.h> +#include <QtGui/private/qopenglcontext_p.h> +#include <QtGui/private/qopenglextensions_p.h> +#include <QtCore/qsharedpointer.h> +#include <QtCore/qhash.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QSGDepthStencilBufferManager; + +class QSGDepthStencilBuffer +{ +public: + enum Attachment + { + NoAttachment = 0x00, + DepthAttachment = 0x01, + StencilAttachment = 0x02 + }; + Q_DECLARE_FLAGS(Attachments, Attachment) + + struct Format + { + QSize size; + int samples; + QSGDepthStencilBuffer::Attachments attachments; + bool operator == (const Format &other) const; + }; + + QSGDepthStencilBuffer(QOpenGLContext *context, const Format &format); + virtual ~QSGDepthStencilBuffer(); + + // Attaches this depth stencil buffer to the currently bound FBO. + void attach(); + // Detaches this depth stencil buffer from the currently bound FBO. + void detach(); + + QSize size() const { return m_format.size; } + int samples() const { return m_format.samples; } + Attachments attachments() const { return m_format.attachments; } + +protected: + virtual void free() = 0; + + QOpenGLExtensions m_functions; + QSGDepthStencilBufferManager *m_manager; + Format m_format; + GLuint m_depthBuffer; + GLuint m_stencilBuffer; + + friend class QSGDepthStencilBufferManager; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGDepthStencilBuffer::Attachments) + +inline bool QSGDepthStencilBuffer::Format::operator == (const Format &other) const +{ + return size == other.size && samples == other.samples && attachments == other.attachments; +} + + +class QSGDefaultDepthStencilBuffer : public QSGDepthStencilBuffer +{ +public: + QSGDefaultDepthStencilBuffer(QOpenGLContext *context, const Format &format); + virtual ~QSGDefaultDepthStencilBuffer(); + +protected: + virtual void free(); +}; + + +class QSGDepthStencilBufferManager +{ +public: + QSGDepthStencilBufferManager(QOpenGLContext *ctx) : m_context(ctx) { } + ~QSGDepthStencilBufferManager(); + QOpenGLContext *context() const { return m_context; } + QSharedPointer<QSGDepthStencilBuffer> bufferForFormat(const QSGDepthStencilBuffer::Format &fmt); + void insertBuffer(const QSharedPointer<QSGDepthStencilBuffer> &buffer); + +private: + typedef QHash<QSGDepthStencilBuffer::Format, QWeakPointer<QSGDepthStencilBuffer> > Hash; + QOpenGLContext *m_context; + Hash m_buffers; + + friend class QSGDepthStencilBuffer; +}; + +extern uint qHash(const QSGDepthStencilBuffer::Format &format); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/quick/scenegraph/util/qsgdistancefieldutil.cpp b/src/quick/scenegraph/util/qsgdistancefieldutil.cpp index 76fdf97d80..ecfdf9b03c 100644 --- a/src/quick/scenegraph/util/qsgdistancefieldutil.cpp +++ b/src/quick/scenegraph/util/qsgdistancefieldutil.cpp @@ -62,16 +62,10 @@ static float defaultAntialiasingSpreadFunc(float glyphScale) return range / glyphScale; } -QSGDistanceFieldGlyphCacheManager::QSGDistanceFieldGlyphCacheManager(QSGContext *c) - : sgCtx(c) - , m_threshold_func(defaultThresholdFunc) +QSGDistanceFieldGlyphCacheManager::QSGDistanceFieldGlyphCacheManager() + : m_threshold_func(defaultThresholdFunc) , m_antialiasingSpread_func(defaultAntialiasingSpreadFunc) { -#ifndef QT_OPENGL_ES - m_defaultAntialiasingMode = QSGGlyphNode::HighQualitySubPixelAntialiasing; -#else - m_defaultAntialiasingMode = QSGGlyphNode::GrayAntialiasing; -#endif } QSGDistanceFieldGlyphCacheManager::~QSGDistanceFieldGlyphCacheManager() @@ -81,11 +75,22 @@ QSGDistanceFieldGlyphCacheManager::~QSGDistanceFieldGlyphCacheManager() QSGDistanceFieldGlyphCache *QSGDistanceFieldGlyphCacheManager::cache(const QRawFont &font) { - QRawFontPrivate *fontD = QRawFontPrivate::get(font); - QHash<QFontEngine *, QSGDistanceFieldGlyphCache *>::iterator cache = m_caches.find(fontD->fontEngine); - if (cache == m_caches.end()) - cache = m_caches.insert(fontD->fontEngine, sgCtx->createDistanceFieldGlyphCache(font)); - return cache.value(); + QString key = QString::fromLatin1("%1_%2_%3_%4") + .arg(font.familyName()) + .arg(font.styleName()) + .arg(font.weight()) + .arg(font.style()); + return m_caches.value(key, 0); +} + +void QSGDistanceFieldGlyphCacheManager::insertCache(const QRawFont &font, QSGDistanceFieldGlyphCache *cache) +{ + QString key = QString::fromLatin1("%1_%2_%3_%4") + .arg(font.familyName()) + .arg(font.styleName()) + .arg(font.weight()) + .arg(font.style()); + m_caches.insert(key, cache); } QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgdistancefieldutil_p.h b/src/quick/scenegraph/util/qsgdistancefieldutil_p.h index 49391a737c..4ed4c28657 100644 --- a/src/quick/scenegraph/util/qsgdistancefieldutil_p.h +++ b/src/quick/scenegraph/util/qsgdistancefieldutil_p.h @@ -58,13 +58,11 @@ class QSGContext; class Q_QUICK_EXPORT QSGDistanceFieldGlyphCacheManager { public: - QSGDistanceFieldGlyphCacheManager(QSGContext *c); + QSGDistanceFieldGlyphCacheManager(); ~QSGDistanceFieldGlyphCacheManager(); QSGDistanceFieldGlyphCache *cache(const QRawFont &font); - - QSGGlyphNode::AntialiasingMode defaultAntialiasingMode() const { return m_defaultAntialiasingMode; } - void setDefaultAntialiasingMode(QSGGlyphNode::AntialiasingMode mode) { m_defaultAntialiasingMode = mode; } + void insertCache(const QRawFont &font, QSGDistanceFieldGlyphCache *cache); ThresholdFunc thresholdFunc() const { return m_threshold_func; } void setThresholdFunc(ThresholdFunc func) { m_threshold_func = func; } @@ -73,9 +71,7 @@ public: void setAntialiasingSpreadFunc(AntialiasingSpreadFunc func) { m_antialiasingSpread_func = func; } private: - QHash<QFontEngine *, QSGDistanceFieldGlyphCache *> m_caches; - - QSGContext *sgCtx; + QHash<QString, QSGDistanceFieldGlyphCache *> m_caches; QSGGlyphNode::AntialiasingMode m_defaultAntialiasingMode; ThresholdFunc m_threshold_func; diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp index adf4242cdc..8dadab620a 100644 --- a/src/quick/util/qquickpath.cpp +++ b/src/quick/util/qquickpath.cpp @@ -527,6 +527,8 @@ QPointF QQuickPath::sequentialPointAt(qreal p, qreal *angle) const QPointF QQuickPath::sequentialPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle) { + Q_ASSERT(p >= 0.0 && p <= 1.0); + if (!prevBez.isValid) return p > .5 ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) : forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle); diff --git a/src/quick/util/qquickpathinterpolator.cpp b/src/quick/util/qquickpathinterpolator.cpp index 2b5fb62872..ee1424b70f 100644 --- a/src/quick/util/qquickpathinterpolator.cpp +++ b/src/quick/util/qquickpathinterpolator.cpp @@ -102,6 +102,8 @@ qreal QQuickPathInterpolator::progress() const void QQuickPathInterpolator::setProgress(qreal progress) { + progress = qMin(qMax(progress, (qreal)0.0), (qreal)1.0); + if (progress == _progress) return; _progress = progress; |