aboutsummaryrefslogtreecommitdiffstats
path: root/src/imports
diff options
context:
space:
mode:
Diffstat (limited to 'src/imports')
-rw-r--r--src/imports/folderlistmodel/fileinfothread_p.h2
-rw-r--r--src/imports/folderlistmodel/fileproperty_p.h20
-rw-r--r--src/imports/folderlistmodel/qquickfolderlistmodel.h12
-rw-r--r--src/imports/imports.pro1
-rw-r--r--src/imports/layouts/qquickgridlayoutengine_p.h6
-rw-r--r--src/imports/layouts/qquicklayout.cpp18
-rw-r--r--src/imports/layouts/qquicklayoutstyleinfo.cpp2
-rw-r--r--src/imports/layouts/qquicklinearlayout.cpp30
-rw-r--r--src/imports/layouts/qquicklinearlayout_p.h6
-rw-r--r--src/imports/localstorage/plugin.cpp42
-rw-r--r--src/imports/settings/qqmlsettings_p.h6
-rw-r--r--src/imports/sharedimage/plugin.cpp134
-rw-r--r--src/imports/sharedimage/qmldir3
-rw-r--r--src/imports/sharedimage/qsharedimageloader.cpp265
-rw-r--r--src/imports/sharedimage/qsharedimageloader_p.h81
-rw-r--r--src/imports/sharedimage/sharedimage.pro17
-rw-r--r--src/imports/sharedimage/sharedimageprovider.cpp156
-rw-r--r--src/imports/sharedimage/sharedimageprovider.h58
-rw-r--r--src/imports/statemachine/signaltransition.cpp2
-rw-r--r--src/imports/statemachine/state.cpp2
-rw-r--r--src/imports/statemachine/state.h4
-rw-r--r--src/imports/statemachine/statemachine.cpp2
-rw-r--r--src/imports/statemachine/statemachine.h4
-rw-r--r--src/imports/statemachine/timeouttransition.cpp2
-rw-r--r--src/imports/statemachine/timeouttransition.h4
-rw-r--r--src/imports/testlib/TestCase.qml258
-rw-r--r--src/imports/testlib/main.cpp1
-rw-r--r--src/imports/testlib/qmldir1
-rw-r--r--src/imports/testlib/toucheventsequence.qdoc110
-rw-r--r--src/imports/xmllistmodel/qqmlxmllistmodel.cpp12
-rw-r--r--src/imports/xmllistmodel/qqmlxmllistmodel_p.h18
31 files changed, 1173 insertions, 106 deletions
diff --git a/src/imports/folderlistmodel/fileinfothread_p.h b/src/imports/folderlistmodel/fileinfothread_p.h
index 75da12a421..b505ece750 100644
--- a/src/imports/folderlistmodel/fileinfothread_p.h
+++ b/src/imports/folderlistmodel/fileinfothread_p.h
@@ -94,7 +94,7 @@ public Q_SLOTS:
#endif
protected:
- void run();
+ void run() override;
void getFileInfos(const QString &path);
void findChangeRange(const QList<FileProperty> &list, int &fromIndex, int &toIndex);
diff --git a/src/imports/folderlistmodel/fileproperty_p.h b/src/imports/folderlistmodel/fileproperty_p.h
index f385cdfdb4..48be4a3d85 100644
--- a/src/imports/folderlistmodel/fileproperty_p.h
+++ b/src/imports/folderlistmodel/fileproperty_p.h
@@ -57,17 +57,17 @@
class FileProperty
{
public:
- FileProperty(const QFileInfo &info)
+ FileProperty(const QFileInfo &info) :
+ mFileName(info.fileName()),
+ mFilePath(info.filePath()),
+ mBaseName(info.baseName()),
+ mSuffix(info.completeSuffix()),
+ mSize(info.size()),
+ mIsDir(info.isDir()),
+ mIsFile(info.isFile()),
+ mLastModified(info.lastModified()),
+ mLastRead(info.lastRead())
{
- mFileName = info.fileName();
- mFilePath = info.filePath();
- mBaseName = info.baseName();
- mSize = info.size();
- mSuffix = info.completeSuffix();
- mIsDir = info.isDir();
- mIsFile = info.isFile();
- mLastModified = info.lastModified();
- mLastRead = info.lastRead();
}
~FileProperty()
{}
diff --git a/src/imports/folderlistmodel/qquickfolderlistmodel.h b/src/imports/folderlistmodel/qquickfolderlistmodel.h
index aae6df452d..dee73dff3e 100644
--- a/src/imports/folderlistmodel/qquickfolderlistmodel.h
+++ b/src/imports/folderlistmodel/qquickfolderlistmodel.h
@@ -94,10 +94,10 @@ public:
FileUrlRole = Qt::UserRole + 9
};
- virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
- virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
- virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
- virtual QHash<int, QByteArray> roleNames() const;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ QHash<int, QByteArray> roleNames() const override;
//![abslistmodel]
//![count]
@@ -144,8 +144,8 @@ public:
Q_INVOKABLE int indexOf(const QUrl &file) const;
//![parserstatus]
- virtual void classBegin();
- virtual void componentComplete();
+ void classBegin() override;
+ void componentComplete() override;
//![parserstatus]
int roleFromString(const QString &roleName) const;
diff --git a/src/imports/imports.pro b/src/imports/imports.pro
index b030cbcf73..45719df874 100644
--- a/src/imports/imports.pro
+++ b/src/imports/imports.pro
@@ -17,6 +17,7 @@ qtHaveModule(quick) {
layouts \
qtquick2 \
window \
+ sharedimage \
testlib
qtConfig(quick-sprite):qtConfig(opengl(es1|es2)?): \
diff --git a/src/imports/layouts/qquickgridlayoutengine_p.h b/src/imports/layouts/qquickgridlayoutengine_p.h
index 86f56a5af4..2810e2e5d0 100644
--- a/src/imports/layouts/qquickgridlayoutengine_p.h
+++ b/src/imports/layouts/qquickgridlayoutengine_p.h
@@ -67,7 +67,7 @@ public:
: QGridLayoutItem(row, column, rowSpan, columnSpan, alignment), m_item(item), sizeHintCacheDirty(true), useFallbackToWidthOrHeight(true) {}
- QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
+ QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const override
{
Q_UNUSED(constraint); // Quick Layouts does not support constraint atm
return effectiveSizeHints()[which];
@@ -99,12 +99,12 @@ public:
sizeHintCacheDirty = true;
}
- QLayoutPolicy::Policy sizePolicy(Qt::Orientation orientation) const
+ QLayoutPolicy::Policy sizePolicy(Qt::Orientation orientation) const override
{
return QQuickLayout::effectiveSizePolicy_helper(m_item, orientation, attachedLayoutObject(m_item, false));
}
- void setGeometry(const QRectF &rect)
+ void setGeometry(const QRectF &rect) override
{
QQuickLayoutAttached *info = attachedLayoutObject(m_item, false);
const QRectF r = info ? rect.marginsRemoved(info->qMargins()) : rect;
diff --git a/src/imports/layouts/qquicklayout.cpp b/src/imports/layouts/qquicklayout.cpp
index fd2ff4a73e..df64b593d9 100644
--- a/src/imports/layouts/qquicklayout.cpp
+++ b/src/imports/layouts/qquicklayout.cpp
@@ -42,6 +42,7 @@
#include <QtCore/qcoreapplication.h>
#include <QtCore/private/qnumeric_p.h>
#include <QtCore/qmath.h>
+#include <QtQml/qqmlinfo.h>
#include <limits>
/*!
@@ -678,7 +679,7 @@ QQuickLayout *QQuickLayoutAttached::parentLayout() const
parentItem = parentItem->parentItem();
return qobject_cast<QQuickLayout *>(parentItem);
} else {
- qWarning("Layout must be attached to Item elements");
+ qmlWarning(parent()) << "Layout must be attached to Item elements";
}
return 0;
}
@@ -695,13 +696,20 @@ QQuickLayout::QQuickLayout(QQuickLayoutPrivate &dd, QQuickItem *parent)
{
}
+static QQuickItemPrivate::ChangeTypes changeTypes =
+ QQuickItemPrivate::SiblingOrder
+ | QQuickItemPrivate::ImplicitWidth
+ | QQuickItemPrivate::ImplicitHeight
+ | QQuickItemPrivate::Destroyed
+ | QQuickItemPrivate::Visibility;
+
QQuickLayout::~QQuickLayout()
{
d_func()->m_isReady = false;
const auto childItems = d_func()->childItems;
for (QQuickItem *child : childItems)
- QQuickItemPrivate::get(child)->removeItemChangeListener(this, QQuickItemPrivate::SiblingOrder);
+ QQuickItemPrivate::get(child)->removeItemChangeListener(this, changeTypes);
}
QQuickLayoutAttached *QQuickLayout::qmlAttachedProperties(QObject *object)
@@ -765,14 +773,14 @@ void QQuickLayout::itemChange(ItemChange change, const ItemChangeData &value)
Q_D(QQuickLayout);
QQuickItem *item = value.item;
qmlobject_connect(item, QQuickItem, SIGNAL(baselineOffsetChanged(qreal)), this, QQuickLayout, SLOT(invalidateSenderItem()));
- QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::SiblingOrder | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight | QQuickItemPrivate::Destroyed | QQuickItemPrivate::Visibility);
+ QQuickItemPrivate::get(item)->addItemChangeListener(this, changeTypes);
d->m_hasItemChangeListeners = true;
if (isReady())
updateLayoutItems();
} else if (change == ItemChildRemovedChange) {
QQuickItem *item = value.item;
qmlobject_disconnect(item, QQuickItem, SIGNAL(baselineOffsetChanged(qreal)), this, QQuickLayout, SLOT(invalidateSenderItem()));
- QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::SiblingOrder | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight | QQuickItemPrivate::Destroyed | QQuickItemPrivate::Visibility);
+ QQuickItemPrivate::get(item)->removeItemChangeListener(this, changeTypes);
if (isReady())
updateLayoutItems();
}
@@ -820,7 +828,7 @@ void QQuickLayout::deactivateRecur()
// When deleting a layout with children, there is no reason for the children to inform the layout that their
// e.g. visibility got changed. The layout already knows that all its children will eventually become invisible, so
// we therefore remove its change listener.
- QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::SiblingOrder | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight | QQuickItemPrivate::Destroyed | QQuickItemPrivate::Visibility);
+ QQuickItemPrivate::get(item)->removeItemChangeListener(this, changeTypes);
if (QQuickLayout *layout = qobject_cast<QQuickLayout*>(item))
layout->deactivateRecur();
}
diff --git a/src/imports/layouts/qquicklayoutstyleinfo.cpp b/src/imports/layouts/qquicklayoutstyleinfo.cpp
index c33ceffb2d..5c8be8f306 100644
--- a/src/imports/layouts/qquicklayoutstyleinfo.cpp
+++ b/src/imports/layouts/qquicklayoutstyleinfo.cpp
@@ -50,7 +50,7 @@ QQuickLayoutStyleInfo::QQuickLayoutStyleInfo()
qreal QQuickLayoutStyleInfo::spacing(Qt::Orientation /*orientation*/) const
{
-#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_BLACKBERRY) || defined(Q_OS_QNX) || defined(Q_OS_WINRT)
+#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_QNX) || defined(Q_OS_WINRT)
// On Android and iOS the default spacing between each UI element is 8dp
qreal spacing = 8.0;
#else
diff --git a/src/imports/layouts/qquicklinearlayout.cpp b/src/imports/layouts/qquicklinearlayout.cpp
index 50b3eed87e..887b9b1fa1 100644
--- a/src/imports/layouts/qquicklinearlayout.cpp
+++ b/src/imports/layouts/qquicklinearlayout.cpp
@@ -41,6 +41,7 @@
#include "qquickgridlayoutengine_p.h"
#include "qquicklayoutstyleinfo_p.h"
#include <QtCore/private/qnumeric_p.h>
+#include <QtQml/qqmlinfo.h>
#include "qdebug.h"
#include <limits>
@@ -664,34 +665,25 @@ void QQuickGridLayout::insertLayoutItems()
int &columnSpan = span[0];
int &rowSpan = span[1];
- bool invalidRowColumn = false;
if (info) {
if (info->isRowSet() || info->isColumnSet()) {
// If row is specified and column is not specified (or vice versa),
// the unspecified component of the cell position should default to 0
- row = column = 0;
- if (info->isRowSet()) {
- row = info->row();
- invalidRowColumn = row < 0;
- }
- if (info->isColumnSet()) {
- column = info->column();
- invalidRowColumn = column < 0;
- }
- }
- if (invalidRowColumn) {
- qWarning("QQuickGridLayoutBase::insertLayoutItems: invalid row/column: %d",
- row < 0 ? row : column);
- return;
+ // The getters do this for us, as they will return 0 for an
+ // unset (or negative) value.
+ row = info->row();
+ column = info->column();
}
rowSpan = info->rowSpan();
columnSpan = info->columnSpan();
- if (columnSpan < 1 || rowSpan < 1) {
- qWarning("QQuickGridLayoutBase::addItem: invalid row span/column span: %d",
- rowSpan < 1 ? rowSpan : columnSpan);
+ if (columnSpan < 1) {
+ qmlWarning(child) << "Layout: invalid column span: " << columnSpan;
return;
- }
+ } else if (rowSpan < 1) {
+ qmlWarning(child) << "Layout: invalid row span: " << rowSpan;
+ return;
+ }
alignment = info->alignment();
}
diff --git a/src/imports/layouts/qquicklinearlayout_p.h b/src/imports/layouts/qquicklinearlayout_p.h
index c289416540..f796c8a855 100644
--- a/src/imports/layouts/qquicklinearlayout_p.h
+++ b/src/imports/layouts/qquicklinearlayout_p.h
@@ -152,12 +152,12 @@ public:
int rows() const;
void setRows(int rows);
- Q_ENUMS(Flow)
enum Flow { LeftToRight, TopToBottom };
+ Q_ENUM(Flow)
Flow flow() const;
void setFlow(Flow flow);
- void insertLayoutItems();
+ void insertLayoutItems() override;
signals:
void columnSpacingChanged();
@@ -199,7 +199,7 @@ public:
qreal spacing() const;
void setSpacing(qreal spacing);
- void insertLayoutItems();
+ void insertLayoutItems() override;
signals:
void spacingChanged();
diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp
index 9191700b45..8679750842 100644
--- a/src/imports/localstorage/plugin.cpp
+++ b/src/imports/localstorage/plugin.cpp
@@ -219,24 +219,6 @@ QQmlSqlDatabaseData::~QQmlSqlDatabaseData()
{
}
-static QString qmlsqldatabase_databasesPath(QV4::ExecutionEngine *engine)
-{
- return engine->qmlEngine()->offlineStoragePath() +
- QDir::separator() + QLatin1String("Databases");
-}
-
-static void qmlsqldatabase_initDatabasesPath(QV4::ExecutionEngine *engine)
-{
- QString databasesPath = qmlsqldatabase_databasesPath(engine);
- if (!QDir().mkpath(databasesPath))
- qWarning() << "LocalStorage: can't create path - " << databasesPath;
-}
-
-static QString qmlsqldatabase_databaseFile(const QString& connectionName, QV4::ExecutionEngine *engine)
-{
- return qmlsqldatabase_databasesPath(engine) + QDir::separator() + connectionName;
-}
-
static ReturnedValue qmlsqldatabase_rows_index(const QQmlSqlDatabaseWrapper *r, ExecutionEngine *v4, quint32 index, bool *hasProperty = 0)
{
Scope scope(v4);
@@ -450,7 +432,8 @@ static ReturnedValue qmlsqldatabase_changeVersion(CallContext *ctx)
if (ok) {
*w->d()->version = to_version;
#if QT_CONFIG(settings)
- QSettings ini(qmlsqldatabase_databaseFile(db.connectionName(), scope.engine) + QLatin1String(".ini"), QSettings::IniFormat);
+ const QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(scope.engine->qmlEngine());
+ QSettings ini(enginePrivate->offlineStorageDatabaseDirectory() + db.connectionName() + QLatin1String(".ini"), QSettings::IniFormat);
ini.setValue(QLatin1String("Version"), to_version);
#endif
}
@@ -723,24 +706,23 @@ void QQuickLocalStorage::openDatabaseSync(QQmlV4Function *args)
if (scope.engine->qmlEngine()->offlineStoragePath().isEmpty())
V4THROW_SQL2(SQLEXCEPTION_DATABASE_ERR, QQmlEngine::tr("SQL: can't create database, offline storage is disabled."));
- qmlsqldatabase_initDatabasesPath(scope.engine);
-
- QSqlDatabase database;
-
QV4::ScopedValue v(scope);
QString dbname = (v = (*args)[0])->toQStringNoThrow();
QString dbversion = (v = (*args)[1])->toQStringNoThrow();
QString dbdescription = (v = (*args)[2])->toQStringNoThrow();
int dbestimatedsize = (v = (*args)[3])->toInt32();
FunctionObject *dbcreationCallback = (v = (*args)[4])->as<FunctionObject>();
-
- QCryptographicHash md5(QCryptographicHash::Md5);
- md5.addData(dbname.toUtf8());
- QString dbid(QLatin1String(md5.result().toHex()));
-
- QString basename = qmlsqldatabase_databaseFile(dbid, scope.engine);
+ QString basename = args->v4engine()->qmlEngine()->offlineStorageDatabaseFilePath(dbname);
+ QFileInfo dbFile(basename);
+ if (!QDir().mkpath(dbFile.dir().absolutePath())) {
+ const QString message = QQmlEngine::tr("LocalStorage: can't create path %1").
+ arg(QDir::toNativeSeparators(dbFile.dir().absolutePath()));
+ V4THROW_SQL2(SQLEXCEPTION_DATABASE_ERR, message);
+ }
+ QString dbid = dbFile.fileName();
bool created = false;
QString version = dbversion;
+ QSqlDatabase database;
{
QSettings ini(basename+QLatin1String(".ini"),QSettings::IniFormat);
@@ -812,7 +794,7 @@ public:
}
void registerTypes(const char *uri) Q_DECL_OVERRIDE
{
- Q_ASSERT(QLatin1String(uri) == "QtQuick.LocalStorage");
+ Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.LocalStorage"));
qmlRegisterSingletonType<QQuickLocalStorage>(uri, 2, 0, "LocalStorage", module_api_factory);
}
};
diff --git a/src/imports/settings/qqmlsettings_p.h b/src/imports/settings/qqmlsettings_p.h
index 0ed55579dd..ce942d7564 100644
--- a/src/imports/settings/qqmlsettings_p.h
+++ b/src/imports/settings/qqmlsettings_p.h
@@ -74,10 +74,10 @@ public:
void setCategory(const QString &category);
protected:
- void timerEvent(QTimerEvent *event);
+ void timerEvent(QTimerEvent *event) override;
- void classBegin();
- void componentComplete();
+ void classBegin() override;
+ void componentComplete() override;
private:
Q_DISABLE_COPY(QQmlSettings)
diff --git a/src/imports/sharedimage/plugin.cpp b/src/imports/sharedimage/plugin.cpp
new file mode 100644
index 0000000000..f20edc641c
--- /dev/null
+++ b/src/imports/sharedimage/plugin.cpp
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qqmlextensionplugin.h>
+#include <qqmlengine.h>
+#include <sharedimageprovider.h>
+
+
+/*!
+ \qmlmodule QtQuick.SharedImage 1
+ \title Qt Quick Shared Image Provider
+ \ingroup qmlmodules
+ \brief Adds an image provider which utilizes shared CPU memory
+
+ \section2 Summary
+
+ This module provides functionality to save memory in use cases where
+ several Qt Quick applications use the same local image files. It does this
+ by placing the decoded QImage data in shared system memory, making it
+ accessible to all the processes (see QSharedMemory).
+
+ This module only shares CPU memory. It does not provide sharing of GPU
+ memory or textures.
+
+ \section2 Usage
+
+ To use this module, import it like this:
+ \code
+ import QtQuick.SharedImage 1.0
+ \endcode
+
+ The sharing functionality is provided through a QQuickImageProvider. Use
+ the "image:" scheme for the URL source of the image, followed by the
+ identifier \e shared, followed by the image file path. For example:
+
+ \code
+ Image { source: "image://shared/usr/share/wallpapers/mybackground.jpg" }
+ \endcode
+
+ This will look for the file \e /usr/share/wallpapers/mybackground.jpg.
+ The first process that does this will read the image file
+ using normal Qt image loading. The decoded image data will then be placed
+ in shared memory, using the full file path as key. Later processes
+ requesting the same image will discover that the data is already available
+ in shared memory. They will then use that instead of loading the image file
+ again.
+
+ The shared image data will be kept available until the last process has deleted
+ its last reference to the shared image, at which point it is automatically released.
+
+ If system memory sharing is not available, the shared image provider falls
+ back to normal, unshared image loading.
+
+ The file path must be absolute. To use a relative path, make it absolute
+ using \e Qt.resolvedUrl() and replace the URL scheme. For example:
+
+ \code
+ ...
+ property string imagePrefix: Qt.resolvedUrl("../myimages/").replace("file://", "image://shared/")
+ Image { source: imagePrefix + "myimage.png" }
+ \endcode
+
+ The shared image module does not provide any directly usable QML types.
+*/
+
+static void initResources()
+{
+#ifdef QT_STATIC
+ Q_INIT_RESOURCE(qmake_QtQuick_SharedImage);
+#endif
+}
+
+QT_BEGIN_NAMESPACE
+
+class QtQuickSharedImagePlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
+public:
+ QtQuickSharedImagePlugin(QObject *parent = 0) : QQmlExtensionPlugin(parent) { initResources(); }
+
+ void registerTypes(const char *uri) Q_DECL_OVERRIDE
+ {
+ Q_ASSERT(uri == QStringLiteral("QtQuick.SharedImage"));
+ // Need to register *something* to let our version number be known:
+ qmlRegisterTypeNotAvailable(uri, 1, 0, "nosuchtype", QStringLiteral("Just a dummy type, do not use"));
+ }
+
+ void initializeEngine(QQmlEngine *engine, const char *uri) override
+ {
+ Q_UNUSED(uri);
+ engine->addImageProvider("shared", new SharedImageProvider);
+ }
+};
+
+QT_END_NAMESPACE
+
+#include "plugin.moc"
diff --git a/src/imports/sharedimage/qmldir b/src/imports/sharedimage/qmldir
new file mode 100644
index 0000000000..64a5aa8ac1
--- /dev/null
+++ b/src/imports/sharedimage/qmldir
@@ -0,0 +1,3 @@
+module QtQuick.SharedImage
+plugin sharedimageplugin
+classname QtQuickSharedImagePlugin
diff --git a/src/imports/sharedimage/qsharedimageloader.cpp b/src/imports/sharedimage/qsharedimageloader.cpp
new file mode 100644
index 0000000000..65cbd92bb4
--- /dev/null
+++ b/src/imports/sharedimage/qsharedimageloader.cpp
@@ -0,0 +1,265 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsharedimageloader_p.h"
+#include <private/qobject_p.h>
+#include <private/qimage_p.h>
+#include <QSharedMemory>
+
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcSharedImage, "qt.quick.sharedimage");
+
+struct SharedImageHeader {
+ quint8 magic;
+ quint8 version;
+ quint16 offset;
+ qint32 width;
+ qint32 height;
+ qint32 bpl;
+ QImage::Format format;
+};
+Q_STATIC_ASSERT(sizeof(SharedImageHeader) % 4 == 0);
+
+#ifndef QT_NO_SHAREDMEMORY
+struct SharedImageInfo {
+ QString path;
+ QPointer<QSharedMemory> shmp;
+};
+
+void cleanupSharedImage(void *cleanupInfo)
+{
+ if (!cleanupInfo)
+ return;
+ SharedImageInfo *sii = static_cast<SharedImageInfo *>(cleanupInfo);
+ qCDebug(lcSharedImage) << "Cleanup called for" << sii->path;
+ if (sii->shmp.isNull()) {
+ qCDebug(lcSharedImage) << "shm is 0 for" << sii->path;
+ return;
+ }
+ QSharedMemory *shm = sii->shmp.data();
+ sii->shmp.clear();
+ delete shm; // destructor detaches
+ delete sii;
+}
+#else
+void cleanupSharedImage(void *) {}
+#endif
+
+class QSharedImageLoaderPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QSharedImageLoader)
+
+public:
+ QSharedImageLoaderPrivate()
+ : QObjectPrivate()
+ {}
+
+ QImage load(const QString &path, QSharedImageLoader::ImageParameters *params);
+
+ void storeImageToMem(void *data, const QImage &img);
+
+ bool verifyMem(const void *data, int size);
+
+ QImage createImageFromMem(const void *data, void *cleanupInfo);
+
+};
+
+
+void QSharedImageLoaderPrivate::storeImageToMem(void *data, const QImage &img)
+{
+ Q_ASSERT(data && !img.isNull());
+
+ SharedImageHeader *h = static_cast<SharedImageHeader *>(data);
+ h->magic = 'Q';
+ h->version = 1;
+ h->offset = sizeof(SharedImageHeader);
+ h->width = img.width();
+ h->height = img.height();
+ h->bpl = img.bytesPerLine();
+ h->format = img.format();
+
+ uchar *p = static_cast<uchar *>(data) + sizeof(SharedImageHeader);
+ memcpy(p, img.constBits(), img.byteCount());
+}
+
+
+bool QSharedImageLoaderPrivate::verifyMem(const void *data, int size)
+{
+ if (!data || size < int(sizeof(SharedImageHeader)))
+ return false;
+
+ const SharedImageHeader *h = static_cast<const SharedImageHeader *>(data);
+ if ((h->magic != 'Q')
+ || (h->version < 1)
+ || (h->offset < sizeof(SharedImageHeader))
+ || (h->width <= 0)
+ || (h->height <= 0)
+ || (h->bpl <= 0)
+ || (h->format <= QImage::Format_Invalid)
+ || (h->format >= QImage::NImageFormats)) {
+ return false;
+ }
+
+ int availSize = size - h->offset;
+ if (h->height * h->bpl > availSize)
+ return false;
+ if ((qt_depthForFormat(h->format) * h->width * h->height) > (8 * availSize))
+ return false;
+
+ return true;
+}
+
+
+QImage QSharedImageLoaderPrivate::createImageFromMem(const void *data, void *cleanupInfo)
+{
+ const SharedImageHeader *h = static_cast<const SharedImageHeader *>(data);
+ const uchar *p = static_cast<const uchar *>(data) + h->offset;
+
+ QImage img(p, h->width, h->height, h->bpl, h->format, cleanupSharedImage, cleanupInfo);
+ return img;
+}
+
+
+QImage QSharedImageLoaderPrivate::load(const QString &path, QSharedImageLoader::ImageParameters *params)
+{
+#ifndef QT_NO_SHAREDMEMORY
+ Q_Q(QSharedImageLoader);
+
+ QImage nil;
+ if (path.isEmpty())
+ return nil;
+
+ QScopedPointer<QSharedMemory> shm(new QSharedMemory(q->key(path, params)));
+ bool locked = false;
+
+ if (!shm->attach(QSharedMemory::ReadOnly)) {
+ QImage img = q->loadFile(path, params);
+ if (img.isNull())
+ return nil;
+ int size = sizeof(SharedImageHeader) + img.byteCount();
+ if (shm->create(size)) {
+ qCDebug(lcSharedImage) << "Created new shm segment of size" << size << "for image" << path;
+ if (!shm->lock()) {
+ qCDebug(lcSharedImage) << "Lock1 failed!?" << shm->errorString();
+ return nil;
+ }
+ locked = true;
+ storeImageToMem(shm->data(), img);
+ } else if (shm->error() == QSharedMemory::AlreadyExists) {
+ // race handling: other process may have created the share while
+ // we loaded the image, so try again to just attach
+ if (!shm->attach(QSharedMemory::ReadOnly)) {
+ qCDebug(lcSharedImage) << "Attach to existing failed?" << shm->errorString();
+ return nil;
+ }
+ } else {
+ qCDebug(lcSharedImage) << "Create failed?" << shm->errorString();
+ return nil;
+ }
+ }
+
+ Q_ASSERT(shm->isAttached());
+
+ if (!locked) {
+ if (!shm->lock()) {
+ qCDebug(lcSharedImage) << "Lock2 failed!?" << shm->errorString();
+ return nil;
+ }
+ locked = true;
+ }
+
+ if (!verifyMem(shm->constData(), shm->size())) {
+ qCDebug(lcSharedImage) << "Verifymem failed!?";
+ shm->unlock();
+ return nil;
+ }
+
+ QSharedMemory *shmp = shm.take();
+ SharedImageInfo *sii = new SharedImageInfo;
+ sii->path = path;
+ sii->shmp = shmp;
+ QImage shImg = createImageFromMem(shmp->constData(), sii);
+
+ if (!shmp->unlock()) {
+ qCDebug(lcSharedImage) << "UnLock failed!?";
+ }
+
+ return shImg;
+#else
+ Q_UNUSED(path);
+ Q_UNUSED(params);
+ return QImage();
+#endif
+}
+
+
+QSharedImageLoader::QSharedImageLoader(QObject *parent)
+ : QObject(*new QSharedImageLoaderPrivate, parent)
+{
+}
+
+QSharedImageLoader::~QSharedImageLoader()
+{
+}
+
+QImage QSharedImageLoader::load(const QString &path, ImageParameters *params)
+{
+ Q_D(QSharedImageLoader);
+
+ return d->load(path, params);
+}
+
+QImage QSharedImageLoader::loadFile(const QString &path, ImageParameters *params)
+{
+ Q_UNUSED(params);
+
+ return QImage(path);
+}
+
+QString QSharedImageLoader::key(const QString &path, ImageParameters *params)
+{
+ Q_UNUSED(params);
+
+ return path;
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/imports/sharedimage/qsharedimageloader_p.h b/src/imports/sharedimage/qsharedimageloader_p.h
new file mode 100644
index 0000000000..afb50e5088
--- /dev/null
+++ b/src/imports/sharedimage/qsharedimageloader_p.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSHAREDIMAGELOADER_H
+#define QSHAREDIMAGELOADER_H
+
+#include <QImage>
+#include <QVariant>
+#include <QLoggingCategory>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcSharedImage);
+
+class QSharedImageLoaderPrivate;
+
+class QSharedImageLoader : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QSharedImageLoader)
+
+public:
+ enum ImageParameter {
+ OriginalSize = 0,
+ RequestedSize,
+ NumImageParameters
+ };
+ typedef QVector<QVariant> ImageParameters;
+
+ QSharedImageLoader(QObject *parent = Q_NULLPTR);
+ ~QSharedImageLoader();
+
+ QImage load(const QString &path, ImageParameters *params = Q_NULLPTR);
+
+protected:
+ virtual QImage loadFile(const QString &path, ImageParameters *params);
+ virtual QString key(const QString &path, ImageParameters *params);
+
+private:
+ Q_DISABLE_COPY(QSharedImageLoader)
+};
+
+QT_END_NAMESPACE
+
+#endif // QSHAREDIMAGELOADER_H
diff --git a/src/imports/sharedimage/sharedimage.pro b/src/imports/sharedimage/sharedimage.pro
new file mode 100644
index 0000000000..523de66ac1
--- /dev/null
+++ b/src/imports/sharedimage/sharedimage.pro
@@ -0,0 +1,17 @@
+CXX_MODULE = qml
+TARGET = sharedimageplugin
+TARGETPATH = QtQuick/SharedImage
+IMPORT_VERSION = 1.0
+
+QT *= quick qml gui-private core-private
+
+SOURCES += \
+ plugin.cpp \
+ sharedimageprovider.cpp \
+ qsharedimageloader.cpp
+
+HEADERS += \
+ sharedimageprovider.h \
+ qsharedimageloader_p.h
+
+load(qml_plugin)
diff --git a/src/imports/sharedimage/sharedimageprovider.cpp b/src/imports/sharedimage/sharedimageprovider.cpp
new file mode 100644
index 0000000000..2dd3a130e9
--- /dev/null
+++ b/src/imports/sharedimage/sharedimageprovider.cpp
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <sharedimageprovider.h>
+#include <qsharedimageloader_p.h>
+#include <qquickimageprovider.h>
+#include <private/qimage_p.h>
+#include <QImageReader>
+#include <QFileInfo>
+#include <QDir>
+
+class QuickSharedImageLoader : public QSharedImageLoader
+{
+ Q_OBJECT
+ friend class SharedImageProvider;
+
+public:
+ QuickSharedImageLoader(QObject *parent = Q_NULLPTR)
+ : QSharedImageLoader(parent)
+ {
+ }
+
+protected:
+ QImage loadFile(const QString &path, ImageParameters *params) override
+ {
+ QImageReader imgio(path);
+ QSize realSize = imgio.size();
+ QSize requestSize = params ? params->value(RequestedSize).toSize() : QSize();
+
+ // Following qquickpixmapcache's readImage, from here...
+ const bool force_scale = imgio.format() == "svg" || imgio.format() == "svgz";
+
+ if (requestSize.width() > 0 || requestSize.height() > 0) {
+ QSize s = realSize;
+ qreal ratio = 0.0;
+ if (requestSize.width() && (force_scale || requestSize.width() < s.width())) {
+ ratio = qreal(requestSize.width())/s.width();
+ }
+ if (requestSize.height() && (force_scale || requestSize.height() < s.height())) {
+ qreal hr = qreal(requestSize.height())/s.height();
+ if (ratio == 0.0 || hr < ratio)
+ ratio = hr;
+ }
+ if (ratio > 0.0) {
+ s.setHeight(qRound(s.height() * ratio));
+ s.setWidth(qRound(s.width() * ratio));
+ imgio.setScaledSize(s);
+ }
+ }
+ // ... to here
+
+ QImage image;
+ if (imgio.read(&image)) {
+ if (realSize.isEmpty())
+ realSize = image.size();
+ // Make sure we have acceptable format for texture uploader, or it will convert & lose sharing
+ // This mimics the testing & conversion normally done by the quick pixmapcache & texturefactory
+ if (image.format() != QImage::Format_RGB32 && image.format() != QImage::Format_ARGB32_Premultiplied) {
+ QImage::Format newFmt = QImage::Format_RGB32;
+ if (image.hasAlphaChannel() && image.data_ptr()->checkForAlphaPixels())
+ newFmt = QImage::Format_ARGB32_Premultiplied;
+ qCDebug(lcSharedImage) << "Convert on load from format" << image.format() << "to" << newFmt;
+ image = image.convertToFormat(newFmt);
+ }
+ }
+
+ if (params && params->count() > OriginalSize)
+ params->replace(OriginalSize, realSize);
+
+ return image;
+ }
+
+ QString key(const QString &path, ImageParameters *params) override
+ {
+ QSize reqSz = params->value(RequestedSize).toSize();
+ if (!reqSz.isValid())
+ return path;
+
+ QString key = path + QStringLiteral("_%1x%2").arg(reqSz.width()).arg(reqSz.height());
+ qCDebug(lcSharedImage) << "KEY:" << key;
+ return key;
+ }
+};
+
+
+SharedImageProvider::SharedImageProvider()
+ : QQuickImageProvider(QQuickImageProvider::Image), loader(new QuickSharedImageLoader)
+{
+}
+
+QImage SharedImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
+{
+ QFileInfo fi(QDir::root(), id);
+ QString path = fi.canonicalFilePath();
+ if (path.isEmpty())
+ return QImage();
+
+ QSharedImageLoader::ImageParameters params(QSharedImageLoader::NumImageParameters);
+ params[QSharedImageLoader::RequestedSize].setValue(requestedSize);
+
+ QImage img = loader->load(path, &params);
+ if (img.isNull()) {
+ // May be sharing problem, fall back to normal local load
+ img = loader->loadFile(path, &params);
+ if (!img.isNull())
+ qCWarning(lcSharedImage) << "Sharing problem; loading" << id << "unshared";
+ }
+
+ //... QSize realSize = params.value(QSharedImageLoader::OriginalSize).toSize();
+ // quickpixmapcache's readImage() reports back the original size, prior to requestedSize scaling, in the *size
+ // parameter. That value is currently ignored by quick however, which only cares about the present size of the
+ // returned image. So handling and sharing of info on pre-scaled size is currently not implemented.
+ if (size) {
+ *size = img.size();
+ }
+
+ return img;
+}
+
+#include "sharedimageprovider.moc"
diff --git a/src/imports/sharedimage/sharedimageprovider.h b/src/imports/sharedimage/sharedimageprovider.h
new file mode 100644
index 0000000000..a2f6b6ef2f
--- /dev/null
+++ b/src/imports/sharedimage/sharedimageprovider.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SHAREDIMAGEPROVIDER_H
+#define SHAREDIMAGEPROVIDER_H
+
+#include <QQuickImageProvider>
+#include <QScopedPointer>
+
+class QuickSharedImageLoader;
+
+class SharedImageProvider : public QQuickImageProvider
+{
+public:
+ SharedImageProvider();
+
+ QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override;
+
+protected:
+ QScopedPointer<QuickSharedImageLoader> loader;
+};
+#endif // SHAREDIMAGEPROVIDER_H
diff --git a/src/imports/statemachine/signaltransition.cpp b/src/imports/statemachine/signaltransition.cpp
index 2e6381fc08..44fbf69431 100644
--- a/src/imports/statemachine/signaltransition.cpp
+++ b/src/imports/statemachine/signaltransition.cpp
@@ -127,7 +127,7 @@ void SignalTransition::setSignal(const QJSValue &signal)
Q_ASSERT(sender);
signalMethod = sender->metaObject()->method(signalObject->signalIndex());
} else {
- qmlInfo(this) << tr("Specified signal does not exist.");
+ qmlWarning(this) << tr("Specified signal does not exist.");
return;
}
diff --git a/src/imports/statemachine/state.cpp b/src/imports/statemachine/state.cpp
index 09d246cc1e..f1294b0de0 100644
--- a/src/imports/statemachine/state.cpp
+++ b/src/imports/statemachine/state.cpp
@@ -54,7 +54,7 @@ void State::componentComplete()
static bool once = false;
if (!once) {
once = true;
- qmlInfo(this) << "No top level StateMachine found. Nothing will run without a StateMachine.";
+ qmlWarning(this) << "No top level StateMachine found. Nothing will run without a StateMachine.";
}
}
}
diff --git a/src/imports/statemachine/state.h b/src/imports/statemachine/state.h
index 73e4ecda6b..8e8cefab19 100644
--- a/src/imports/statemachine/state.h
+++ b/src/imports/statemachine/state.h
@@ -58,8 +58,8 @@ class State : public QState, public QQmlParserStatus
public:
explicit State(QState *parent = 0);
- void classBegin() {}
- void componentComplete();
+ void classBegin() override {}
+ void componentComplete() override;
QQmlListProperty<QObject> children();
diff --git a/src/imports/statemachine/statemachine.cpp b/src/imports/statemachine/statemachine.cpp
index 76de01fe94..a9ea5f7a95 100644
--- a/src/imports/statemachine/statemachine.cpp
+++ b/src/imports/statemachine/statemachine.cpp
@@ -69,7 +69,7 @@ void StateMachine::setRunning(bool running)
void StateMachine::componentComplete()
{
if (QStateMachine::initialState() == NULL && childMode() == QState::ExclusiveStates)
- qmlInfo(this) << "No initial state set for StateMachine";
+ qmlWarning(this) << "No initial state set for StateMachine";
// Everything is proper setup, now start the state-machine if we got
// asked to do so.
diff --git a/src/imports/statemachine/statemachine.h b/src/imports/statemachine/statemachine.h
index 59a810f387..cb0ab43f8b 100644
--- a/src/imports/statemachine/statemachine.h
+++ b/src/imports/statemachine/statemachine.h
@@ -62,8 +62,8 @@ class StateMachine : public QStateMachine, public QQmlParserStatus
public:
explicit StateMachine(QObject *parent = 0);
- void classBegin() {}
- void componentComplete();
+ void classBegin() override {}
+ void componentComplete() override;
QQmlListProperty<QObject> children();
bool isRunning() const;
diff --git a/src/imports/statemachine/timeouttransition.cpp b/src/imports/statemachine/timeouttransition.cpp
index 4bb1df3c28..0d208b919b 100644
--- a/src/imports/statemachine/timeouttransition.cpp
+++ b/src/imports/statemachine/timeouttransition.cpp
@@ -72,7 +72,7 @@ void TimeoutTransition::componentComplete()
{
QState *state = qobject_cast<QState*>(parent());
if (!state) {
- qmlInfo(this) << "Parent needs to be a State";
+ qmlWarning(this) << "Parent needs to be a State";
return;
}
diff --git a/src/imports/statemachine/timeouttransition.h b/src/imports/statemachine/timeouttransition.h
index 5d71e86bb4..0e5f5377e3 100644
--- a/src/imports/statemachine/timeouttransition.h
+++ b/src/imports/statemachine/timeouttransition.h
@@ -59,8 +59,8 @@ public:
int timeout() const;
void setTimeout(int timeout);
- void classBegin() {}
- void componentComplete();
+ void classBegin() override {}
+ void componentComplete() override;
Q_SIGNALS:
void timeoutChanged();
diff --git a/src/imports/testlib/TestCase.qml b/src/imports/testlib/TestCase.qml
index d22ec7c44f..18c70e1169 100644
--- a/src/imports/testlib/TestCase.qml
+++ b/src/imports/testlib/TestCase.qml
@@ -206,6 +206,59 @@ import Qt.test.qtestroot 1.0
will fail. Use the \l when and windowShown properties to track
when the main window has been shown.
+ \section1 Managing Dynamically Created Test Objects
+
+ A typical pattern with QML tests is to
+ \l {Dynamic QML Object Creation from JavaScript}{dynamically create}
+ an item and then destroy it at the end of the test function:
+
+ \code
+ TestCase {
+ id: testCase
+ name: "MyTest"
+ when: windowShown
+
+ function test_click() {
+ var item = Qt.createQmlObject("import QtQuick 2.0; Item {}", testCase);
+ verify(item);
+
+ // Test item...
+
+ item.destroy();
+ }
+ }
+ \endcode
+
+ The problem with this pattern is that any failures in the test function
+ will cause the call to \c item.destroy() to be skipped, leaving the item
+ hanging around in the scene until the test case has finished. This can
+ result in interference with future tests; for example, by blocking input
+ events or producing unrelated debug output that makes it difficult to
+ follow the code's execution.
+
+ By calling \l createTemporaryQmlObject() instead, the object is guaranteed
+ to be destroyed at the end of the test function:
+
+ \code
+ TestCase {
+ id: testCase
+ name: "MyTest"
+ when: windowShown
+
+ function test_click() {
+ var item = createTemporaryQmlObject("import QtQuick 2.0; Item {}", testCase);
+ verify(item);
+
+ // Test item...
+
+ // Don't need to worry about destroying "item" here.
+ }
+ }
+ \endcode
+
+ For objects that are created via the \l {Component::}{createObject()} function
+ of \l Component, the \l createTemporaryObject() function can be used.
+
\sa {QtTest::SignalSpy}{SignalSpy}, {Qt Quick Test Reference Documentation}
*/
@@ -358,6 +411,8 @@ Item {
/*! \internal */
property var qtest_events: qtest_events_normal
TestEvent { id: qtest_events_normal }
+ /*! \internal */
+ property var qtest_temporaryObjects: []
/*!
\qmlmethod TestCase::fail(message = "")
@@ -461,6 +516,105 @@ Item {
throw new Error("QtQuickTest::fail")
}
+ /*!
+ \since 5.9
+ \qmlmethod object TestCase::createTemporaryQmlObject(string qml, object parent, string filePath)
+
+ This function dynamically creates a QML object from the given \a qml
+ string with the specified \a parent. The returned object will be
+ destroyed (if it was not already) after \l cleanup() has finished
+ executing, meaning that objects created with this function are
+ guaranteed to be destroyed after each test, regardless of whether or
+ not the tests fail.
+
+ If there was an error while creating the object, \c null will be
+ returned.
+
+ If \a filePath is specified, it will be used for error reporting for
+ the created object.
+
+ This function calls
+ \l {QtQml::Qt::createQmlObject()}{Qt.createQmlObject()} internally.
+
+ \sa {Managing Dynamically Created Test Objects}
+ */
+ function createTemporaryQmlObject(qml, parent, filePath) {
+ if (typeof qml !== "string") {
+ qtest_results.fail("First argument must be a string of QML; actual type is " + typeof qml,
+ util.callerFile(), util.callerLine());
+ throw new Error("QtQuickTest::fail");
+ }
+
+ if (!parent || typeof parent !== "object") {
+ qtest_results.fail("Second argument must be a valid parent object; actual type is " + typeof parent,
+ util.callerFile(), util.callerLine());
+ throw new Error("QtQuickTest::fail");
+ }
+
+ if (filePath !== undefined && typeof filePath !== "string") {
+ qtest_results.fail("Third argument must be a file path string; actual type is " + typeof filePath,
+ util.callerFile(), util.callerLine());
+ throw new Error("QtQuickTest::fail");
+ }
+
+ var object = Qt.createQmlObject(qml, parent, filePath);
+ qtest_temporaryObjects.push(object);
+ return object;
+ }
+
+ /*!
+ \since 5.9
+ \qmlmethod object TestCase::createTemporaryObject(Component component, object parent, object properties)
+
+ This function dynamically creates a QML object from the given
+ \a component with the specified optional \a parent and \a properties.
+ The returned object will be destroyed (if it was not already) after
+ \l cleanup() has finished executing, meaning that objects created with
+ this function are guaranteed to be destroyed after each test,
+ regardless of whether or not the tests fail.
+
+ If there was an error while creating the object, \c null will be
+ returned.
+
+ This function calls
+ \l {QtQml::Component::createObject()}{component.createObject()}
+ internally.
+
+ \sa {Managing Dynamically Created Test Objects}
+ */
+ function createTemporaryObject(component, parent, properties) {
+ if (typeof component !== "object") {
+ qtest_results.fail("First argument must be a Component; actual type is " + typeof component,
+ util.callerFile(), util.callerLine());
+ throw new Error("QtQuickTest::fail");
+ }
+
+ if (properties && typeof properties !== "object") {
+ qtest_results.fail("Third argument must be an object; actual type is " + typeof properties,
+ util.callerFile(), util.callerLine());
+ throw new Error("QtQuickTest::fail");
+ }
+
+ var object = component.createObject(parent, properties ? properties : ({}));
+ qtest_temporaryObjects.push(object);
+ return object;
+ }
+
+ /*!
+ \internal
+
+ Destroys all temporary objects that still exist.
+ */
+ function qtest_destroyTemporaryObjects() {
+ for (var i = 0; i < qtest_temporaryObjects.length; ++i) {
+ var temporaryObject = qtest_temporaryObjects[i];
+ // ### the typeof check can be removed when QTBUG-57749 is fixed
+ if (temporaryObject && typeof temporaryObject.destroy === "function")
+ temporaryObject.destroy();
+ }
+ qtest_temporaryObjects = [];
+ }
+
/*! \internal */
// Determine what is o.
// Discussions and reference: http://philrathe.com/articles/equiv
@@ -1317,6 +1471,109 @@ Item {
qtest_fail("window not shown", 2)
}
+ /*!
+ \qmlmethod TouchEventSequence TestCase::touchEvent(object item)
+
+ \since 5.9
+
+ Begins a sequence of touch events through a simulated QTouchDevice::TouchScreen.
+ Events are delivered to the window containing \a item.
+
+ The returned object is used to enumerate events to be delivered through a single
+ QTouchEvent. Touches are delivered to the window containing the TestCase unless
+ otherwise specified.
+
+ \code
+ Rectangle {
+ width: 640; height: 480
+
+ MultiPointTouchArea {
+ id: area
+ anchors.fill: parent
+
+ property bool touched: false
+
+ onPressed: touched = true
+ }
+
+ TestCase {
+ name: "ItemTests"
+ when: area.pressed
+ id: test1
+
+ function test_touch() {
+ var touch = touchEvent(area);
+ touch.press(0, area, 10, 10);
+ touch.commit();
+ verify(area.touched);
+ }
+ }
+ }
+ \endcode
+
+ \sa TouchEventSequence::press(), TouchEventSequence::move(), TouchEventSequence::release(), TouchEventSequence::stationary(), TouchEventSequence::commit(), QTouchDevice::TouchScreen
+ */
+
+ function touchEvent(item) {
+ if (!item)
+ qtest_fail("No item given to touchEvent", 1)
+
+ return {
+ _defaultItem: item,
+ _sequence: qtest_events.touchEvent(item),
+
+ press: function (id, target, x, y) {
+ if (!target)
+ target = this._defaultItem;
+ if (id === undefined)
+ qtest_fail("No id given to TouchEventSequence::press", 1);
+ if (x === undefined)
+ x = target.width / 2;
+ if (y === undefined)
+ y = target.height / 2;
+ this._sequence.press(id, target, x, y);
+ return this;
+ },
+
+ move: function (id, target, x, y) {
+ if (!target)
+ target = this._defaultItem;
+ if (id === undefined)
+ qtest_fail("No id given to TouchEventSequence::move", 1);
+ if (x === undefined)
+ x = target.width / 2;
+ if (y === undefined)
+ y = target.height / 2;
+ this._sequence.move(id, target, x, y);
+ return this;
+ },
+
+ stationary: function (id) {
+ if (id === undefined)
+ qtest_fail("No id given to TouchEventSequence::stationary", 1);
+ this._sequence.stationary(id);
+ return this;
+ },
+
+ release: function (id, target, x, y) {
+ if (!target)
+ target = this._defaultItem;
+ if (id === undefined)
+ qtest_fail("No id given to TouchEventSequence::release", 1);
+ if (x === undefined)
+ x = target.width / 2;
+ if (y === undefined)
+ y = target.height / 2;
+ this._sequence.release(id, target, x, y);
+ return this;
+ },
+
+ commit: function () {
+ this._sequence.commit();
+ return this;
+ }
+ };
+ }
// Functions that can be overridden in subclasses for init/cleanup duties.
/*!
@@ -1389,6 +1646,7 @@ Item {
qtest_runInternal(prop, arg)
qtest_results.finishTestData()
qtest_runInternal("cleanup")
+ qtest_destroyTemporaryObjects()
qtest_results.finishTestDataCleanup()
// wait(0) will call processEvents() so objects marked for deletion
// in the test function will be deleted.
diff --git a/src/imports/testlib/main.cpp b/src/imports/testlib/main.cpp
index 4e2bd919e9..3c28000e35 100644
--- a/src/imports/testlib/main.cpp
+++ b/src/imports/testlib/main.cpp
@@ -158,6 +158,7 @@ public:
qmlRegisterType<QuickTestResult, 1>(uri,1,1,"TestResult");
qmlRegisterType<QuickTestEvent>(uri,1,0,"TestEvent");
qmlRegisterType<QuickTestUtil>(uri,1,0,"TestUtil");
+ qmlRegisterType<QQuickTouchEventSequence>();
}
};
diff --git a/src/imports/testlib/qmldir b/src/imports/testlib/qmldir
index 9da8ebb4be..e5757f6a88 100644
--- a/src/imports/testlib/qmldir
+++ b/src/imports/testlib/qmldir
@@ -3,4 +3,5 @@ plugin qmltestplugin
classname QTestQmlModule
typeinfo plugins.qmltypes
TestCase 1.0 TestCase.qml
+TestCase 1.2 TestCase.qml
SignalSpy 1.0 SignalSpy.qml
diff --git a/src/imports/testlib/toucheventsequence.qdoc b/src/imports/testlib/toucheventsequence.qdoc
new file mode 100644
index 0000000000..f85a1cd4f9
--- /dev/null
+++ b/src/imports/testlib/toucheventsequence.qdoc
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Jeremy Katz
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \qmltype TouchEventSequence
+ \inqmlmodule QtTest
+ \ingroup qtquicktest
+ \brief TouchEventSequence is used to build and dispatch touch events
+ for testing.
+
+ \since 5.9
+
+ A TouchEventSequence is created by calling \l [QML] {TestCase::touchEvent()}{TestCase.touchEvent()}.
+ The type can not be directly instantiated. Each method provided by the type returns
+ the same object, allowing chained calls.
+
+ For example:
+ \code
+ touchEvent(item).press(0).commit();
+ \endcode
+ is equivalent to:
+ \code
+ var sequence = touchEvent(item);
+ sequence.press(0);
+ sequence.commit();
+ \endcode
+
+ Events are delivered to the window which contains the item specified in touchEvent.
+
+ \sa TestCase::touchEvent(), QTest::QTouchEventSequence
+*/
+
+/*!
+ \qmlmethod TouchEventSequence TouchEventSequence::press(int touchId, object item, real x = item.width / 2, real y = item.height / 2)
+
+ Creates a new point identified as \a touchId, at the point indicated by \a x and \a y relative to \a item.
+ Further use of the same touch point should maintain the same touchId.
+
+ Item defaults to the value provided via touchEvent().
+ X and y default to the midpoint of the item.
+*/
+
+/*!
+ \qmlmethod TouchEventSequence TouchEventSequence::move(int touchId, object item, real x = item.width / 2, real y = item.height / 2)
+
+ Moves \a touchId to the point indicated by \a x and \a y relative to \a item.
+
+ Item defaults to the value provided via touchEvent().
+ X and y default to the midpoint of the item.
+*/
+
+/*!
+ \qmlmethod TouchEventSequence TouchEventSequence::release(int touchId, object item, real x = item.width / 2, real y = item.height / 2)
+
+ Removes \a touchId at the point indicated by \a x and \a y relative to \a item.
+
+ Item defaults to the value provided via touchEvent().
+ X and y default to the midpoint of the item.
+*/
+
+/*!
+ \qmlmethod TouchEventSequence TouchEventSequence::stationary(int touchId)
+
+ Indicates that \a touchId is present but otherwise unchanged from prior events.
+*/
+
+/*!
+ \qmlmethod TouchEventSequence TouchEventSequence::commit()
+
+ Sends the touch event composed by prior use of press(), move(), release(), and stationary().
+ Following commit's return, the TouchEventSequence can be used to compose a new event.
+
+ \code
+ var sequence = touchEvent(target);
+ // Touch the middle of target with 1 point
+ sequence.press(1);
+ sequence.commit();
+
+ // Begin a new event
+ // Move the point to target's upper left corner
+ sequence.move(1, target, 0, 0);
+ sequence.commit();
+ \endcode
+
+ Commit is automatically invoked when the TouchEventSequence object is destroyed.
+*/
diff --git a/src/imports/xmllistmodel/qqmlxmllistmodel.cpp b/src/imports/xmllistmodel/qqmlxmllistmodel.cpp
index 6e9e57a046..61c8665a14 100644
--- a/src/imports/xmllistmodel/qqmlxmllistmodel.cpp
+++ b/src/imports/xmllistmodel/qqmlxmllistmodel.cpp
@@ -176,7 +176,7 @@ public:
QQuickXmlQueryThreadObject(QQuickXmlQueryEngine *);
void processJobs();
- virtual bool event(QEvent *e);
+ bool event(QEvent *e) override;
private:
QQuickXmlQueryEngine *m_queryEngine;
@@ -202,7 +202,7 @@ signals:
void error(void*, const QString&);
protected:
- void run();
+ void run() override;
private:
void processQuery(XmlQueryJob *job);
@@ -596,7 +596,7 @@ void QQuickXmlListModelPrivate::append_role(QQmlListProperty<QQuickXmlListModelR
int i = _this->d_func()->roleObjects.count();
_this->d_func()->roleObjects.append(role);
if (_this->d_func()->roleNames.contains(role->name())) {
- qmlInfo(role) << QQuickXmlListModel::tr("\"%1\" duplicates a previous role name and will be disabled.").arg(role->name());
+ qmlWarning(role) << QQuickXmlListModel::tr("\"%1\" duplicates a previous role name and will be disabled.").arg(role->name());
return;
}
_this->d_func()->roles.insert(i, _this->d_func()->highestRole);
@@ -847,7 +847,7 @@ void QQuickXmlListModel::setQuery(const QString &query)
{
Q_D(QQuickXmlListModel);
if (!query.startsWith(QLatin1Char('/'))) {
- qmlInfo(this) << QCoreApplication::translate("QQuickXmlRoleList", "An XmlListModel query must start with '/' or \"//\"");
+ qmlWarning(this) << QCoreApplication::translate("QQuickXmlRoleList", "An XmlListModel query must start with '/' or \"//\"");
return;
}
@@ -1136,11 +1136,11 @@ void QQuickXmlListModel::queryError(void* object, const QString& error)
Q_D(QQuickXmlListModel);
for (int i=0; i<d->roleObjects.count(); i++) {
if (d->roleObjects.at(i) == static_cast<QQuickXmlListModelRole*>(object)) {
- qmlInfo(d->roleObjects.at(i)) << QQuickXmlListModel::tr("invalid query: \"%1\"").arg(error);
+ qmlWarning(d->roleObjects.at(i)) << QQuickXmlListModel::tr("invalid query: \"%1\"").arg(error);
return;
}
}
- qmlInfo(this) << QQuickXmlListModel::tr("invalid query: \"%1\"").arg(error);
+ qmlWarning(this) << QQuickXmlListModel::tr("invalid query: \"%1\"").arg(error);
}
void QQuickXmlListModel::queryCompleted(const QQuickXmlQueryResult &result)
diff --git a/src/imports/xmllistmodel/qqmlxmllistmodel_p.h b/src/imports/xmllistmodel/qqmlxmllistmodel_p.h
index f0096a9125..e6a0898bb9 100644
--- a/src/imports/xmllistmodel/qqmlxmllistmodel_p.h
+++ b/src/imports/xmllistmodel/qqmlxmllistmodel_p.h
@@ -79,7 +79,6 @@ class QQuickXmlListModel : public QAbstractListModel, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
- Q_ENUMS(Status)
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged)
@@ -95,10 +94,10 @@ public:
QQuickXmlListModel(QObject *parent = 0);
~QQuickXmlListModel();
- QModelIndex index(int row, int column, const QModelIndex &parent) const;
- int rowCount(const QModelIndex &parent) const;
- QVariant data(const QModelIndex &index, int role) const;
- QHash<int, QByteArray> roleNames() const;
+ QModelIndex index(int row, int column, const QModelIndex &parent) const override;
+ int rowCount(const QModelIndex &parent) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ QHash<int, QByteArray> roleNames() const override;
int count() const;
@@ -119,13 +118,14 @@ public:
Q_INVOKABLE QQmlV4Handle get(int index) const;
enum Status { Null, Ready, Loading, Error };
+ Q_ENUM(Status)
Status status() const;
qreal progress() const;
Q_INVOKABLE QString errorString() const;
- virtual void classBegin();
- virtual void componentComplete();
+ void classBegin() override;
+ void componentComplete() override;
Q_SIGNALS:
void statusChanged(QQuickXmlListModel::Status);
@@ -177,7 +177,7 @@ public:
void setQuery(const QString &query)
{
if (query.startsWith(QLatin1Char('/'))) {
- qmlInfo(this) << tr("An XmlRole query must not start with '/'");
+ qmlWarning(this) << tr("An XmlRole query must not start with '/'");
return;
}
if (m_query == query)
@@ -194,7 +194,7 @@ public:
Q_EMIT isKeyChanged();
}
- bool isValid() {
+ bool isValid() const {
return !m_name.isEmpty() && !m_query.isEmpty();
}