aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2018-07-09 10:26:49 +0200
committerUlf Hermann <ulf.hermann@qt.io>2018-07-13 10:21:37 +0000
commitac6bb3170d86006832170377cd5f51ff809b4455 (patch)
treea4ff77347e9c5b87e94a19f827e7248201e7bd93 /src/plugins
parent8fdfd9ff5ded388a3d29c917aebb795485abfbfd (diff)
Tooling: Add QML preview debug service
Task-number: QDS-181 Change-Id: I02193afb84aa111792d8bebff3bdd9b410f9db5a Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qmldbg_preview.pro29
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp202
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.h89
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp432
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.h125
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp181
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.h105
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp313
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h92
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp102
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.h76
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp150
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h97
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.json3
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.cpp39
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.h58
-rw-r--r--src/plugins/qmltooling/qmltooling.pro5
17 files changed, 2097 insertions, 1 deletions
diff --git a/src/plugins/qmltooling/qmldbg_preview/qmldbg_preview.pro b/src/plugins/qmltooling/qmldbg_preview/qmldbg_preview.pro
new file mode 100644
index 0000000000..08686a43e3
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qmldbg_preview.pro
@@ -0,0 +1,29 @@
+QT += core-private qml-private packetprotocol-private network quick-private gui-private
+
+TARGET = qmldbg_preview
+
+SOURCES += \
+ $$PWD/qqmlpreviewblacklist.cpp \
+ $$PWD/qqmlpreviewfileengine.cpp \
+ $$PWD/qqmlpreviewfileloader.cpp \
+ $$PWD/qqmlpreviewhandler.cpp \
+ $$PWD/qqmlpreviewposition.cpp \
+ $$PWD/qqmlpreviewservice.cpp \
+ $$PWD/qqmlpreviewservicefactory.cpp
+
+HEADERS += \
+ $$PWD/qqmlpreviewblacklist.h \
+ $$PWD/qqmlpreviewfileengine.h \
+ $$PWD/qqmlpreviewfileloader.h \
+ $$PWD/qqmlpreviewhandler.h \
+ $$PWD/qqmlpreviewposition.h \
+ $$PWD/qqmlpreviewservice.h \
+ $$PWD/qqmlpreviewservicefactory.h
+
+OTHER_FILES += \
+ $$PWD/qqmlpreviewservice.json
+
+PLUGIN_TYPE = qmltooling
+PLUGIN_CLASS_NAME = QQmlPreviewServiceFactory
+
+load(qt_plugin)
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp
new file mode 100644
index 0000000000..d942740db3
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlpreviewblacklist.h"
+
+QT_BEGIN_NAMESPACE
+
+void QQmlPreviewBlacklist::blacklist(const QString &path)
+{
+ if (!path.isEmpty())
+ m_root.insert(path, 0);
+}
+
+void QQmlPreviewBlacklist::whitelist(const QString &path)
+{
+ if (!path.isEmpty())
+ m_root.remove(path, 0);
+}
+
+bool QQmlPreviewBlacklist::isBlacklisted(const QString &path) const
+{
+ return path.isEmpty() ? true : m_root.containedPrefixLeaf(path, 0) > 0;
+}
+
+void QQmlPreviewBlacklist::clear()
+{
+ m_root = Node();
+}
+
+QQmlPreviewBlacklist::Node::Node()
+{
+}
+
+QQmlPreviewBlacklist::Node::Node(const QQmlPreviewBlacklist::Node &other) :
+ m_mine(other.m_mine), m_isLeaf(other.m_isLeaf)
+{
+ for (auto it = other.m_next.begin(), end = other.m_next.end(); it != end; ++it)
+ m_next.insert(it.key(), new Node(**it));
+}
+
+QQmlPreviewBlacklist::Node::Node(QQmlPreviewBlacklist::Node &&other) Q_DECL_NOEXCEPT
+{
+ m_mine.swap(other.m_mine);
+ m_next.swap(other.m_next);
+ m_isLeaf = other.m_isLeaf;
+}
+
+QQmlPreviewBlacklist::Node::~Node()
+{
+ qDeleteAll(m_next);
+}
+
+QQmlPreviewBlacklist::Node &QQmlPreviewBlacklist::Node::operator=(
+ const QQmlPreviewBlacklist::Node &other)
+{
+ if (&other != this) {
+ m_mine = other.m_mine;
+ for (auto it = other.m_next.begin(), end = other.m_next.end(); it != end; ++it)
+ m_next.insert(it.key(), new Node(**it));
+ m_isLeaf = other.m_isLeaf;
+ }
+ return *this;
+}
+
+QQmlPreviewBlacklist::Node &QQmlPreviewBlacklist::Node::operator=(
+ QQmlPreviewBlacklist::Node &&other) Q_DECL_NOEXCEPT
+{
+ if (&other != this) {
+ m_mine.swap(other.m_mine);
+ m_next.swap(other.m_next);
+ m_isLeaf = other.m_isLeaf;
+ }
+ return *this;
+}
+
+void QQmlPreviewBlacklist::Node::split(QString::iterator it, QString::iterator end)
+{
+ QString existing;
+ existing.resize(end - it - 1);
+ std::copy(it + 1, end, existing.begin());
+
+ Node *node = new Node(existing, m_next, m_isLeaf);
+ m_next.clear();
+ m_next.insert(*it, node);
+ m_mine.resize(it - m_mine.begin());
+ m_isLeaf = false;
+}
+
+void QQmlPreviewBlacklist::Node::insert(const QString &path, int offset)
+{
+ for (auto it = m_mine.begin(), end = m_mine.end(); it != end; ++it) {
+ if (offset == path.size()) {
+ split(it, end);
+ m_isLeaf = true;
+ return;
+ }
+
+ if (path.at(offset) != *it) {
+ split(it, end);
+
+ QString inserted;
+ inserted.resize(path.size() - offset - 1);
+ std::copy(path.begin() + offset + 1, path.end(), inserted.begin());
+ m_next.insert(path.at(offset), new Node(inserted));
+ return;
+ }
+
+ ++offset;
+ }
+
+ if (offset == path.size()) {
+ m_isLeaf = true;
+ return;
+ }
+
+ Node *&node = m_next[path.at(offset++)];
+ if (node == nullptr) {
+ QString inserted;
+ inserted.resize(path.size() - offset);
+ std::copy(path.begin() + offset, path.end(), inserted.begin());
+ node = new Node(inserted);
+ } else {
+ node->insert(path, offset);
+ }
+}
+
+void QQmlPreviewBlacklist::Node::remove(const QString &path, int offset)
+{
+ for (auto it = m_mine.begin(), end = m_mine.end(); it != end; ++it) {
+ if (offset == path.size() || path.at(offset) != *it) {
+ split(it, end);
+ return;
+ }
+ ++offset;
+ }
+
+ m_isLeaf = false;
+ if (offset == path.size())
+ return;
+
+ auto it = m_next.find(path.at(offset));
+ if (it != m_next.end())
+ (*it)->remove(path, ++offset);
+}
+
+int QQmlPreviewBlacklist::Node::containedPrefixLeaf(const QString &path, int offset) const
+{
+ if (offset == path.size())
+ return (m_mine.isEmpty() && m_isLeaf) ? offset : -1;
+
+ for (auto it = m_mine.begin(), end = m_mine.end(); it != end; ++it) {
+ if (path.at(offset) != *it)
+ return -1;
+
+ if (++offset == path.size())
+ return (++it == end && m_isLeaf) ? offset : -1;
+ }
+
+ const QChar c = path.at(offset);
+ if (m_isLeaf && c == '/')
+ return offset;
+
+ auto it = m_next.find(c);
+ if (it == m_next.end())
+ return -1;
+
+ return (*it)->containedPrefixLeaf(path, ++offset);
+}
+
+QQmlPreviewBlacklist::Node::Node(const QString &mine,
+ const QHash<QChar, QQmlPreviewBlacklist::Node *> &next,
+ bool isLeaf)
+ : m_mine(mine), m_next(next), m_isLeaf(isLeaf)
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.h
new file mode 100644
index 0000000000..ab9c3a3d8a
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLPREVIEWBLACKLIST_H
+#define QQMLPREVIEWBLACKLIST_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/qhash.h>
+#include <QtCore/qchar.h>
+#include <QtCore/qstring.h>
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlPreviewBlacklist
+{
+public:
+ void blacklist(const QString &path);
+ void whitelist(const QString &path);
+ bool isBlacklisted(const QString &path) const;
+ void clear();
+
+private:
+ class Node {
+ public:
+ Node();
+ Node(const Node &other);
+ Node(Node &&other) Q_DECL_NOEXCEPT;
+
+ ~Node();
+
+ Node &operator=(const Node &other);
+ Node &operator=(Node &&other) Q_DECL_NOEXCEPT;
+
+ void split(QString::iterator it, QString::iterator end);
+ void insert(const QString &path, int offset);
+ void remove(const QString &path, int offset);
+ int containedPrefixLeaf(const QString &path, int offset) const;
+
+ private:
+ Node(const QString &mine, const QHash<QChar, Node *> &next = QHash<QChar, Node *>(),
+ bool isLeaf = true);
+
+ QString m_mine;
+ QHash<QChar, Node *> m_next;
+ bool m_isLeaf = false;
+ };
+
+ Node m_root;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLPREVIEWBLACKLIST_H
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp
new file mode 100644
index 0000000000..f88cebf806
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp
@@ -0,0 +1,432 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlpreviewfileengine.h"
+#include "qqmlpreviewservice.h"
+
+#include <QtCore/qlibraryinfo.h>
+#include <QtCore/qthread.h>
+#include <QtCore/qwaitcondition.h>
+
+#include <cstring>
+
+QT_BEGIN_NAMESPACE
+
+static bool isRelative(const QString &path)
+{
+ if (path.isEmpty())
+ return true;
+ if (path.at(0) == '/')
+ return false;
+ if (path.at(0) == ':' && path.length() >= 2 && path.at(1) == '/')
+ return false;
+#ifdef Q_OS_WIN
+ if (path.length() >= 2 && path.at(1) == ':')
+ return false;
+#endif
+ return true;
+}
+
+static QString absolutePath(const QString &path)
+{
+ return QDir::cleanPath(isRelative(path) ? (QDir::currentPath() + '/' + path) : path);
+}
+
+bool isRootPath(const QString &path)
+{
+ return QFileSystemEntry::isRootPath(path);
+}
+
+class QQmlPreviewFileEngineIterator : public QAbstractFileEngineIterator
+{
+public:
+ QQmlPreviewFileEngineIterator(QDir::Filters filters, const QStringList &filterNames,
+ const QStringList &m_entries);
+ ~QQmlPreviewFileEngineIterator();
+
+ QString next() override;
+ bool hasNext() const override;
+ QString currentFileName() const override;
+
+private:
+ const QStringList m_entries;
+ int m_index;
+};
+
+QQmlPreviewFileEngineIterator::QQmlPreviewFileEngineIterator(QDir::Filters filters,
+ const QStringList &filterNames,
+ const QStringList &entries)
+ : QAbstractFileEngineIterator(filters, filterNames), m_entries(entries), m_index(0)
+{
+}
+
+QQmlPreviewFileEngineIterator::~QQmlPreviewFileEngineIterator()
+{
+}
+
+QString QQmlPreviewFileEngineIterator::next()
+{
+ if (!hasNext())
+ return QString();
+ ++m_index;
+ return currentFilePath();
+}
+
+bool QQmlPreviewFileEngineIterator::hasNext() const
+{
+ return m_index < m_entries.size();
+}
+
+QString QQmlPreviewFileEngineIterator::currentFileName() const
+{
+ if (m_index == 0 || m_index > m_entries.size())
+ return QString();
+ return m_entries.at(m_index - 1);
+}
+
+QQmlPreviewFileEngine::QQmlPreviewFileEngine(const QString &file, const QString &absolute,
+ QQmlPreviewFileLoader *loader) :
+ m_name(file), m_absolute(absolute), m_loader(loader)
+{
+ load();
+}
+
+void QQmlPreviewFileEngine::setFileName(const QString &file)
+{
+ m_name = file;
+ m_absolute = absolutePath(file);
+ m_fallback.reset();
+ m_contents.close();
+ m_contents.setData(QByteArray());
+ m_entries.clear();
+ load();
+}
+
+bool QQmlPreviewFileEngine::open(QIODevice::OpenMode flags)
+{
+ switch (m_result) {
+ case QQmlPreviewFileLoader::File:
+ return m_contents.open(flags);
+ case QQmlPreviewFileLoader::Directory:
+ return false;
+ case QQmlPreviewFileLoader::Fallback:
+ return m_fallback->open(flags);
+ default:
+ Q_UNREACHABLE();
+ return false;
+ }
+}
+
+bool QQmlPreviewFileEngine::close()
+{
+ switch (m_result) {
+ case QQmlPreviewFileLoader::Fallback:
+ return m_fallback->close();
+ case QQmlPreviewFileLoader::File:
+ m_contents.close();
+ return true;
+ case QQmlPreviewFileLoader::Directory:
+ return false;
+ default:
+ Q_UNREACHABLE();
+ return false;
+ }
+}
+
+qint64 QQmlPreviewFileEngine::size() const
+{
+ return m_fallback ? m_fallback->size() : m_contents.size();
+}
+
+qint64 QQmlPreviewFileEngine::pos() const
+{
+ return m_fallback ? m_fallback->pos() : m_contents.pos();
+}
+
+bool QQmlPreviewFileEngine::seek(qint64 newPos)
+{
+ return m_fallback? m_fallback->seek(newPos) : m_contents.seek(newPos);
+}
+
+qint64 QQmlPreviewFileEngine::read(char *data, qint64 maxlen)
+{
+ return m_fallback ? m_fallback->read(data, maxlen) : m_contents.read(data, maxlen);
+}
+
+QAbstractFileEngine::FileFlags QQmlPreviewFileEngine::fileFlags(
+ QAbstractFileEngine::FileFlags type) const
+{
+ if (m_fallback)
+ return m_fallback->fileFlags(type);
+
+ QAbstractFileEngine::FileFlags ret = 0;
+
+ if (type & PermsMask) {
+ ret |= QAbstractFileEngine::FileFlags(
+ ReadOwnerPerm | ReadUserPerm | ReadGroupPerm | ReadOtherPerm);
+ }
+
+ if (type & TypesMask) {
+ if (m_result == QQmlPreviewFileLoader::Directory)
+ ret |= DirectoryType;
+ else
+ ret |= FileType;
+ }
+
+ if (type & FlagsMask) {
+ ret |= ExistsFlag;
+ if (isRootPath(m_name))
+ ret |= RootFlag;
+ }
+
+ return ret;
+}
+
+QString QQmlPreviewFileEngine::fileName(QAbstractFileEngine::FileName file) const
+{
+ if (m_fallback)
+ return m_fallback->fileName(file);
+
+ if (file == BaseName) {
+ int slashPos = m_name.lastIndexOf('/');
+ if (slashPos == -1)
+ return m_name;
+ return m_name.mid(slashPos + 1);
+ } else if (file == PathName || file == AbsolutePathName) {
+ const QString path = (file == AbsolutePathName) ? m_absolute : m_name;
+ const int slashPos = path.lastIndexOf('/');
+ if (slashPos == -1)
+ return QString();
+ else if (slashPos == 0)
+ return "/";
+ return path.left(slashPos);
+ } else if (file == CanonicalName || file == CanonicalPathName) {
+ if (file == CanonicalPathName) {
+ const int slashPos = m_absolute.lastIndexOf('/');
+ if (slashPos != -1)
+ return m_absolute.left(slashPos);
+ }
+ return m_absolute;
+ }
+ return m_name;
+}
+
+uint QQmlPreviewFileEngine::ownerId(QAbstractFileEngine::FileOwner owner) const
+{
+ return m_fallback ? m_fallback->ownerId(owner) : static_cast<uint>(-2);
+}
+
+QAbstractFileEngine::Iterator *QQmlPreviewFileEngine::beginEntryList(QDir::Filters filters,
+ const QStringList &filterNames)
+{
+ return m_fallback ? m_fallback->beginEntryList(filters, filterNames)
+ : new QQmlPreviewFileEngineIterator(filters, filterNames, m_entries);
+}
+
+QAbstractFileEngine::Iterator *QQmlPreviewFileEngine::endEntryList()
+{
+ return m_fallback ? m_fallback->endEntryList() : nullptr;
+}
+
+bool QQmlPreviewFileEngine::flush()
+{
+ return m_fallback ? m_fallback->flush() : true;
+}
+
+bool QQmlPreviewFileEngine::syncToDisk()
+{
+ return m_fallback ? m_fallback->syncToDisk() : false;
+}
+
+bool QQmlPreviewFileEngine::isSequential() const
+{
+ return m_fallback ? m_fallback->isSequential() : m_contents.isSequential();
+}
+
+bool QQmlPreviewFileEngine::remove()
+{
+ return m_fallback ? m_fallback->remove() : false;
+}
+
+bool QQmlPreviewFileEngine::copy(const QString &newName)
+{
+ return m_fallback ? m_fallback->copy(newName) : false;
+}
+
+bool QQmlPreviewFileEngine::rename(const QString &newName)
+{
+ return m_fallback ? m_fallback->rename(newName) : false;
+}
+
+bool QQmlPreviewFileEngine::renameOverwrite(const QString &newName)
+{
+ return m_fallback ? m_fallback->renameOverwrite(newName) : false;
+}
+
+bool QQmlPreviewFileEngine::link(const QString &newName)
+{
+ return m_fallback ? m_fallback->link(newName) : false;
+}
+
+bool QQmlPreviewFileEngine::mkdir(const QString &dirName, bool createParentDirectories) const
+{
+ return m_fallback ? m_fallback->mkdir(dirName, createParentDirectories) : false;
+}
+
+bool QQmlPreviewFileEngine::rmdir(const QString &dirName, bool recurseParentDirectories) const
+{
+ return m_fallback ? m_fallback->rmdir(dirName, recurseParentDirectories) : false;
+}
+
+bool QQmlPreviewFileEngine::setSize(qint64 size)
+{
+ switch (m_result) {
+ case QQmlPreviewFileLoader::Fallback:
+ return m_fallback->setSize(size);
+ case QQmlPreviewFileLoader::File:
+ if (size < 0 || size > std::numeric_limits<int>::max())
+ return false;
+ m_contents.buffer().resize(static_cast<int>(size));
+ return true;
+ case QQmlPreviewFileLoader::Directory:
+ return false;
+ default:
+ Q_UNREACHABLE();
+ return false;
+ }
+}
+
+bool QQmlPreviewFileEngine::caseSensitive() const
+{
+ return m_fallback ? m_fallback->caseSensitive() : true;
+}
+
+bool QQmlPreviewFileEngine::isRelativePath() const
+{
+ return m_fallback ? m_fallback->isRelativePath() : isRelative(m_name);
+}
+
+QStringList QQmlPreviewFileEngine::entryList(QDir::Filters filters,
+ const QStringList &filterNames) const
+{
+ return m_fallback ? m_fallback->entryList(filters, filterNames)
+ : QAbstractFileEngine::entryList(filters, filterNames);
+}
+
+bool QQmlPreviewFileEngine::setPermissions(uint perms)
+{
+ return m_fallback ? m_fallback->setPermissions(perms) : false;
+}
+
+QByteArray QQmlPreviewFileEngine::id() const
+{
+ return m_fallback ? m_fallback->id() : QByteArray();
+}
+
+QString QQmlPreviewFileEngine::owner(FileOwner owner) const
+{
+ return m_fallback ? m_fallback->owner(owner) : QString();
+}
+
+QDateTime QQmlPreviewFileEngine::fileTime(FileTime time) const
+{
+ // Files we replace are always newer than the ones we had before. This makes the QML engine
+ // actually recompile them, rather than pick them from the cache.
+ return m_fallback ? m_fallback->fileTime(time) : QDateTime::currentDateTime();
+}
+
+int QQmlPreviewFileEngine::handle() const
+{
+ return m_fallback ? m_fallback->handle() : -1;
+}
+
+qint64 QQmlPreviewFileEngine::readLine(char *data, qint64 maxlen)
+{
+ return m_fallback ? m_fallback->readLine(data, maxlen) : m_contents.readLine(data, maxlen);
+}
+
+qint64 QQmlPreviewFileEngine::write(const char *data, qint64 len)
+{
+ return m_fallback ? m_fallback->write(data, len) : m_contents.write(data, len);
+}
+
+bool QQmlPreviewFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output)
+{
+ return m_fallback ? m_fallback->extension(extension, option, output) : false;
+}
+
+bool QQmlPreviewFileEngine::supportsExtension(Extension extension) const
+{
+ return m_fallback ? m_fallback->supportsExtension(extension) : false;
+}
+
+void QQmlPreviewFileEngine::load() const
+{
+ m_result = m_loader->load(m_absolute);
+ switch (m_result) {
+ case QQmlPreviewFileLoader::File:
+ m_contents.setData(m_loader->contents());
+ break;
+ case QQmlPreviewFileLoader::Directory:
+ m_entries = m_loader->entries();
+ break;
+ case QQmlPreviewFileLoader::Fallback:
+ m_fallback.reset(QAbstractFileEngine::create(m_name));
+ break;
+ case QQmlPreviewFileLoader::Unknown:
+ Q_UNREACHABLE();
+ break;
+ }
+}
+
+QQmlPreviewFileEngineHandler::QQmlPreviewFileEngineHandler(QQmlPreviewFileLoader *loader)
+ : m_loader(loader)
+{
+}
+
+QAbstractFileEngine *QQmlPreviewFileEngineHandler::create(const QString &fileName) const
+{
+ // Don't load compiled QML/JS over the network
+ if (fileName.endsWith(".qmlc") || fileName.endsWith(".jsc") || isRootPath(fileName)) {
+ return nullptr;
+ }
+
+ QString relative = fileName;
+ while (relative.endsWith('/'))
+ relative.chop(1);
+
+ if (relative.isEmpty() || relative == ":")
+ return nullptr;
+
+ const QString absolute = relative.startsWith(':') ? relative : absolutePath(relative);
+
+ return m_loader->isBlacklisted(absolute)
+ ? nullptr : new QQmlPreviewFileEngine(relative, absolute, m_loader.data());
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.h
new file mode 100644
index 0000000000..60af76c334
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLPREVIEWFILEENGINE_H
+#define QQMLPREVIEWFILEENGINE_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 "qqmlpreviewfileloader.h"
+
+#include <private/qabstractfileengine_p.h>
+#include <private/qfsfileengine_p.h>
+#include <QtCore/qbuffer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlPreviewFileEngine : public QAbstractFileEngine
+{
+public:
+ QQmlPreviewFileEngine(const QString &file, const QString &absolute,
+ QQmlPreviewFileLoader *loader);
+
+ void setFileName(const QString &file) override;
+
+ bool open(QIODevice::OpenMode flags) override ;
+ bool close() override;
+ qint64 size() const override;
+ qint64 pos() const override;
+ bool seek(qint64) override;
+ qint64 read(char *data, qint64 maxlen) override;
+
+ FileFlags fileFlags(FileFlags type) const override;
+ QString fileName(QAbstractFileEngine::FileName file) const override;
+ uint ownerId(FileOwner) const override;
+
+ Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override;
+ Iterator *endEntryList() override;
+
+ // Forwarding to fallback if exists
+ bool flush() override;
+ bool syncToDisk() override;
+ bool isSequential() const override;
+ bool remove() override;
+ bool copy(const QString &newName) override;
+ bool rename(const QString &newName) override;
+ bool renameOverwrite(const QString &newName) override;
+ bool link(const QString &newName) override;
+ bool mkdir(const QString &dirName, bool createParentDirectories) const override;
+ bool rmdir(const QString &dirName, bool recurseParentDirectories) const override;
+ bool setSize(qint64 size) override;
+ bool caseSensitive() const override;
+ bool isRelativePath() const override;
+ QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const override;
+ bool setPermissions(uint perms) override;
+ QByteArray id() const override;
+ QString owner(FileOwner) const override;
+ QDateTime fileTime(FileTime time) const override;
+ int handle() const override;
+ qint64 readLine(char *data, qint64 maxlen) override;
+ qint64 write(const char *data, qint64 len) override;
+ bool extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output) override;
+ bool supportsExtension(Extension extension) const override;
+
+private:
+ void load() const;
+
+ QString m_name;
+ QString m_absolute;
+ QPointer<QQmlPreviewFileLoader> m_loader;
+
+ mutable QBuffer m_contents;
+ mutable QStringList m_entries;
+ mutable QScopedPointer<QAbstractFileEngine> m_fallback;
+ mutable QQmlPreviewFileLoader::Result m_result = QQmlPreviewFileLoader::Unknown;
+};
+
+class QQmlPreviewFileEngineHandler : public QAbstractFileEngineHandler
+{
+public:
+ QQmlPreviewFileEngineHandler(QQmlPreviewFileLoader *loader);
+ QAbstractFileEngine *create(const QString &fileName) const override;
+
+private:
+ QPointer<QQmlPreviewFileLoader> m_loader;
+};
+
+
+
+QT_END_NAMESPACE
+
+#endif // QQMLPREVIEWFILEENGINE_H
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp
new file mode 100644
index 0000000000..6026d58746
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlpreviewfileloader.h"
+#include "qqmlpreviewservice.h"
+
+#include <QtCore/qlibraryinfo.h>
+#include <QtCore/qstandardpaths.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlPreviewFileLoader::QQmlPreviewFileLoader(QQmlPreviewServiceImpl *service) : m_service(service)
+{
+ // Exclude some resource paths used by Qt itself. There is no point in loading those from the
+ // client as the client will not have the files (or even worse, it may have different ones).
+ m_blacklist.blacklist(":/qt-project.org");
+ m_blacklist.blacklist(":/QtQuick/Controls/Styles");
+ m_blacklist.blacklist(":/ExtrasImports/QtQuick/Controls/Styles");
+
+ for (int loc = QLibraryInfo::PrefixPath; loc <= QLibraryInfo::TestsPath; ++loc) {
+ m_blacklist.blacklist(QLibraryInfo::location(
+ static_cast<QLibraryInfo::LibraryLocation>(loc)));
+ }
+ m_blacklist.blacklist(QLibraryInfo::location(QLibraryInfo::SettingsPath));
+
+ static const QStandardPaths::StandardLocation blackListLocations[] = {
+ QStandardPaths::DataLocation,
+ QStandardPaths::CacheLocation,
+ QStandardPaths::GenericDataLocation,
+ QStandardPaths::RuntimeLocation,
+ QStandardPaths::ConfigLocation,
+ QStandardPaths::GenericCacheLocation,
+ QStandardPaths::GenericConfigLocation,
+ QStandardPaths::AppDataLocation,
+ QStandardPaths::AppConfigLocation
+ };
+
+ for (auto locationType : blackListLocations) {
+ const QStringList locations = QStandardPaths::standardLocations(locationType);
+ for (const QString &location : locations)
+ m_blacklist.blacklist(location);
+ }
+
+ connect(this, &QQmlPreviewFileLoader::request, service, &QQmlPreviewServiceImpl::forwardRequest,
+ Qt::DirectConnection);
+ connect(service, &QQmlPreviewServiceImpl::directory, this, &QQmlPreviewFileLoader::directory);
+ connect(service, &QQmlPreviewServiceImpl::file, this, &QQmlPreviewFileLoader::file);
+ connect(service, &QQmlPreviewServiceImpl::error, this, &QQmlPreviewFileLoader::error);
+ connect(service, &QQmlPreviewServiceImpl::clearCache, this, &QQmlPreviewFileLoader::clearCache);
+ moveToThread(&m_thread);
+ m_thread.start();
+}
+
+QQmlPreviewFileLoader::~QQmlPreviewFileLoader() {
+ m_thread.quit();
+ m_thread.wait();
+}
+
+QQmlPreviewFileLoader::Result QQmlPreviewFileLoader::load(const QString &path)
+{
+ QMutexLocker locker(&m_mutex);
+ m_path = path;
+
+ auto fileIterator = m_fileCache.constFind(path);
+ if (fileIterator != m_fileCache.constEnd()) {
+ m_result = File;
+ m_contents = *fileIterator;
+ m_entries.clear();
+ return m_result;
+ }
+
+ auto dirIterator = m_directoryCache.constFind(path);
+ if (dirIterator != m_directoryCache.constEnd()) {
+ m_result = Directory;
+ m_contents.clear();
+ m_entries = *dirIterator;
+ return m_result;
+ }
+
+ m_result = Unknown;
+ m_entries.clear();
+ m_contents.clear();
+ emit request(path);
+ m_waitCondition.wait(&m_mutex);
+ return m_result;
+}
+
+QByteArray QQmlPreviewFileLoader::contents()
+{
+ QMutexLocker locker(&m_mutex);
+ return m_contents;
+}
+
+QStringList QQmlPreviewFileLoader::entries()
+{
+ QMutexLocker locker(&m_mutex);
+ return m_entries;
+}
+
+void QQmlPreviewFileLoader::whitelist(const QUrl &url)
+{
+ const QString path = QQmlFile::urlToLocalFileOrQrc(url);
+ if (!path.isEmpty()) {
+ QMutexLocker locker(&m_mutex);
+ m_blacklist.whitelist(path);
+ }
+}
+
+bool QQmlPreviewFileLoader::isBlacklisted(const QString &path)
+{
+ QMutexLocker locker(&m_mutex);
+ return m_blacklist.isBlacklisted(path);
+}
+
+void QQmlPreviewFileLoader::file(const QString &path, const QByteArray &contents)
+{
+ QMutexLocker locker(&m_mutex);
+ m_blacklist.whitelist(path);
+ m_fileCache[path] = contents;
+ if (path == m_path) {
+ m_contents = contents;
+ m_result = File;
+ m_waitCondition.wakeOne();
+ }
+}
+
+void QQmlPreviewFileLoader::directory(const QString &path, const QStringList &entries)
+{
+ QMutexLocker locker(&m_mutex);
+ m_blacklist.whitelist(path);
+ m_directoryCache[path] = entries;
+ if (path == m_path) {
+ m_entries = entries;
+ m_result = Directory;
+ m_waitCondition.wakeOne();
+ }
+}
+
+void QQmlPreviewFileLoader::error(const QString &path)
+{
+ QMutexLocker locker(&m_mutex);
+ m_blacklist.blacklist(path);
+ if (path == m_path) {
+ m_result = Fallback;
+ m_waitCondition.wakeOne();
+ }
+}
+
+void QQmlPreviewFileLoader::clearCache()
+{
+ QMutexLocker locker(&m_mutex);
+ m_fileCache.clear();
+ m_directoryCache.clear();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.h
new file mode 100644
index 0000000000..de0a9cadea
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLPREVIEWFILELOADER_H
+#define QQMLPREVIEWFILELOADER_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 "qqmlpreviewblacklist.h"
+
+#include <QtCore/qobject.h>
+#include <QtCore/qthread.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qwaitcondition.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/qset.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlPreviewServiceImpl;
+class QQmlPreviewFileLoader : public QObject
+{
+ Q_OBJECT
+public:
+ enum Result {
+ File,
+ Directory,
+ Fallback,
+ Unknown
+ };
+
+ QQmlPreviewFileLoader(QQmlPreviewServiceImpl *service);
+ ~QQmlPreviewFileLoader();
+
+ Result load(const QString &file);
+ QByteArray contents();
+ QStringList entries();
+
+ void whitelist(const QUrl &url);
+ bool isBlacklisted(const QString &file);
+
+signals:
+ void request(const QString &file);
+
+private:
+ QMutex m_mutex;
+ QWaitCondition m_waitCondition;
+
+ QThread m_thread;
+ QPointer<QQmlPreviewServiceImpl> m_service;
+
+ QString m_path;
+ QByteArray m_contents;
+ QStringList m_entries;
+ Result m_result;
+
+ QQmlPreviewBlacklist m_blacklist;
+ QHash<QString, QByteArray> m_fileCache;
+ QHash<QString, QStringList> m_directoryCache;
+
+ void file(const QString &file, const QByteArray &contents);
+ void directory(const QString &file, const QStringList &entries);
+ void error(const QString &file);
+ void clearCache();
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLPREVIEWFILELOADER_H
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp
new file mode 100644
index 0000000000..fdfa9dcc34
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp
@@ -0,0 +1,313 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlpreviewhandler.h"
+
+#include <QtCore/qtimer.h>
+#include <QtCore/qsettings.h>
+#include <QtGui/qwindow.h>
+#include <QtGui/qguiapplication.h>
+#include <QtQuick/qquickwindow.h>
+#include <QtQuick/qquickitem.h>
+#include <QtQml/qqmlcomponent.h>
+
+#include <private/qquickpixmapcache_p.h>
+#include <private/qquickview_p.h>
+#include <private/qhighdpiscaling_p.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QuitLockDisabler
+{
+ const bool quitLockEnabled;
+
+ QuitLockDisabler() : quitLockEnabled(QCoreApplication::isQuitLockEnabled())
+ {
+ QCoreApplication::setQuitLockEnabled(false);
+ }
+
+ ~QuitLockDisabler()
+ {
+ QCoreApplication::setQuitLockEnabled(quitLockEnabled);
+ }
+};
+
+QQmlPreviewHandler::QQmlPreviewHandler(QObject *parent) : QObject(parent)
+{
+ m_dummyItem.reset(new QQuickItem);
+
+ // TODO: Is there a better way to determine this? We want to keep the window alive when possible
+ // as otherwise it will reappear in a different place when (re)loading a file. However,
+ // the file we load might create another window, in which case the eglfs plugin (and
+ // others?) will do a qFatal as it only supports a single window.
+ const QString platformName = QGuiApplication::platformName();
+ m_supportsMultipleWindows = (platformName == QStringLiteral("windows")
+ || platformName == QStringLiteral("cocoa")
+ || platformName == QStringLiteral("xcb")
+ || platformName == QStringLiteral("wayland"));
+
+ QCoreApplication::instance()->installEventFilter(this);
+}
+
+QQmlPreviewHandler::~QQmlPreviewHandler()
+{
+ clear();
+}
+
+static void closeAllWindows()
+{
+ const QWindowList windows = QGuiApplication::allWindows();
+ for (QWindow *window : windows)
+ window->close();
+}
+
+bool QQmlPreviewHandler::eventFilter(QObject *obj, QEvent *event)
+{
+ if (event->type() == QEvent::Show) {
+ if (QWindow *window = qobject_cast<QQuickWindow*>(obj)) {
+ m_lastPosition.initLastSavedWindowPosition(window);
+ }
+ }
+ if ((event->type() == QEvent::Move || event->type() == QEvent::Resize) &&
+ qobject_cast<QQuickWindow*>(obj) == m_currentWindow) {
+ // we always start with factor 1 so calculate and save the origin as it would be not scaled
+ m_lastPosition.setPosition(m_currentWindow->framePosition() *
+ QHighDpiScaling::factor(m_currentWindow));
+ }
+
+ return QObject::eventFilter(obj, event);
+}
+
+void QQmlPreviewHandler::addEngine(QQmlEngine *qmlEngine)
+{
+ m_engines.append(qmlEngine);
+}
+
+void QQmlPreviewHandler::removeEngine(QQmlEngine *qmlEngine)
+{
+ const bool found = m_engines.removeOne(qmlEngine);
+ Q_ASSERT(found);
+ for (QObject *obj : m_createdObjects)
+ if (obj && QtQml::qmlEngine(obj) == qmlEngine)
+ delete obj;
+ m_createdObjects.removeAll(nullptr);
+}
+
+void QQmlPreviewHandler::loadUrl(const QUrl &url)
+{
+ QSharedPointer<QuitLockDisabler> disabler(new QuitLockDisabler);
+
+ clear();
+ m_component.reset(nullptr);
+ QQuickPixmap::purgeCache();
+
+ const int numEngines = m_engines.count();
+ if (numEngines > 1) {
+ emit error(QString::fromLatin1("%1 QML engines available. We cannot decide which one "
+ "should load the component.").arg(numEngines));
+ return;
+ } else if (numEngines == 0) {
+ emit error(QLatin1String("No QML engines found."));
+ return;
+ }
+ m_lastPosition.loadWindowPositionSettings(url);
+
+ QQmlEngine *engine = m_engines.front();
+ engine->clearComponentCache();
+ m_component.reset(new QQmlComponent(engine, url, this));
+
+ auto onStatusChanged = [disabler, this](QQmlComponent::Status status) {
+ switch (status) {
+ case QQmlComponent::Null:
+ case QQmlComponent::Loading:
+ return true; // try again later
+ case QQmlComponent::Ready:
+ tryCreateObject();
+ break;
+ case QQmlComponent::Error:
+ emit error(m_component->errorString());
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ disconnect(m_component.data(), &QQmlComponent::statusChanged, this, nullptr);
+ return false; // we're done
+ };
+
+ if (onStatusChanged(m_component->status()))
+ connect(m_component.data(), &QQmlComponent::statusChanged, this, onStatusChanged);
+}
+
+void QQmlPreviewHandler::rerun()
+{
+ if (m_component.isNull() || !m_component->isReady())
+ emit error(QLatin1String("Component is not ready."));
+
+ QuitLockDisabler disabler;
+ Q_UNUSED(disabler);
+ clear();
+ tryCreateObject();
+}
+
+void QQmlPreviewHandler::zoom(qreal newFactor)
+{
+ if (!m_currentWindow)
+ return;
+ if (qFuzzyIsNull(newFactor)) {
+ emit error(QString::fromLatin1("Zooming with factor: %1 will result in nothing " \
+ "so it will be ignored.").arg(newFactor));
+ return;
+ }
+ QString errorMessage;
+ bool resetZoom = false;
+
+ if (newFactor < 0) {
+ resetZoom = true;
+ newFactor = 1.0;
+ }
+
+ // On single-window devices we allow any scale factor as the window will adapt to the screen.
+ if (m_supportsMultipleWindows) {
+ const QSize newAvailableScreenSize = QQmlPreviewPosition::currentScreenSize(m_currentWindow)
+ * QHighDpiScaling::factor(m_currentWindow) / newFactor;
+ if (m_currentWindow->size().width() > newAvailableScreenSize.width()) {
+ errorMessage = QString::fromLatin1(
+ "Zooming with factor: "
+ "%1 will result in a too wide preview.").arg(newFactor);
+ }
+ if (m_currentWindow->size().height() > newAvailableScreenSize.height()) {
+ errorMessage = QString::fromLatin1(
+ "Zooming with factor: "
+ "%1 will result in a too heigh preview.").arg(newFactor);
+ }
+ }
+
+ if (errorMessage.isEmpty()) {
+ const QPoint newToOriginMappedPosition = m_currentWindow->position() *
+ QHighDpiScaling::factor(m_currentWindow) / newFactor;
+ m_currentWindow->destroy();
+ QHighDpiScaling::setScreenFactor(m_currentWindow->screen(), newFactor);
+ if (resetZoom)
+ QHighDpiScaling::updateHighDpiScaling();
+ m_currentWindow->setPosition(newToOriginMappedPosition);
+ m_currentWindow->show();
+ } else {
+ emit error(errorMessage);
+ }
+}
+
+void QQmlPreviewHandler::clear()
+{
+ qDeleteAll(m_createdObjects);
+ m_createdObjects.clear();
+ m_currentWindow = nullptr;
+}
+
+Qt::WindowFlags fixFlags(Qt::WindowFlags flags)
+{
+ // If only the type flag is given, some other window flags are automatically assumed. When we
+ // add a flag, we need to make those explicit.
+ switch (flags) {
+ case Qt::Window:
+ return flags | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint
+ | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint;
+ case Qt::Dialog:
+ case Qt::Tool:
+ return flags | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint;
+ default:
+ return flags;
+ }
+}
+
+void QQmlPreviewHandler::showObject(QObject *object)
+{
+ if (QWindow *window = qobject_cast<QWindow *>(object)) {
+ m_currentWindow = qobject_cast<QQuickWindow *>(window);
+ for (QWindow *otherWindow : QGuiApplication::allWindows()) {
+ if (QQuickWindow *quickWindow = qobject_cast<QQuickWindow *>(otherWindow)) {
+ if (quickWindow == m_currentWindow)
+ continue;
+ quickWindow->setVisible(false);
+ quickWindow->setFlags(quickWindow->flags() & ~Qt::WindowStaysOnTopHint);
+ }
+ }
+ } else if (QQuickItem *item = qobject_cast<QQuickItem *>(object)) {
+ m_currentWindow = nullptr;
+ for (QWindow *window : QGuiApplication::allWindows()) {
+ if (QQuickWindow *quickWindow = qobject_cast<QQuickWindow *>(window)) {
+ if (m_currentWindow != nullptr) {
+ emit error(QLatin1String("Multiple QQuickWindows available. We cannot "
+ "decide which one to use."));
+ return;
+ }
+ m_currentWindow = quickWindow;
+ } else {
+ window->setVisible(false);
+ window->setFlag(Qt::WindowStaysOnTopHint, false);
+ }
+ }
+
+ if (m_currentWindow == nullptr) {
+ m_currentWindow = new QQuickWindow;
+ m_createdObjects.append(m_currentWindow.data());
+ }
+
+ for (QQuickItem *oldItem : m_currentWindow->contentItem()->childItems())
+ oldItem->setParentItem(m_dummyItem.data());
+
+ // Special case for QQuickView, as that keeps a "root" pointer around, and uses it to
+ // automatically resize the window or the item.
+ if (QQuickView *view = qobject_cast<QQuickView *>(m_currentWindow))
+ QQuickViewPrivate::get(view)->setRootObject(item);
+ else
+ item->setParentItem(m_currentWindow->contentItem());
+
+ m_currentWindow->resize(item->size().toSize());
+ } else {
+ emit error(QLatin1String("Created object is neither a QWindow nor a QQuickItem."));
+ }
+
+ if (m_currentWindow) {
+ m_lastPosition.initLastSavedWindowPosition(m_currentWindow);
+ m_currentWindow->setFlags(fixFlags(m_currentWindow->flags()) | Qt::WindowStaysOnTopHint);
+ m_currentWindow->setVisible(true);
+ }
+}
+
+void QQmlPreviewHandler::tryCreateObject()
+{
+ if (!m_supportsMultipleWindows)
+ closeAllWindows();
+ QObject *object = m_component->create();
+ m_createdObjects.append(object);
+ showObject(object);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h
new file mode 100644
index 0000000000..78099fb1e5
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLPREVIEWHANDLER_H
+#define QQMLPREVIEWHANDLER_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 "qqmlpreviewposition.h"
+
+#include <QtCore/qobject.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qpointer.h>
+#include <QtQml/qqmlengine.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlEngine;
+class QQuickItem;
+class QQmlPreviewUrlInterceptor;
+class QQuickWindow;
+class QQmlPreviewHandler : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QQmlPreviewHandler(QObject *parent = nullptr);
+ ~QQmlPreviewHandler();
+
+ void addEngine(QQmlEngine *engine);
+ void removeEngine(QQmlEngine *engine);
+
+ void loadUrl(const QUrl &url);
+ void rerun();
+ void zoom(qreal newFactor);
+
+ void clear();
+
+signals:
+ void error(const QString &message);
+protected:
+ bool eventFilter(QObject *obj, QEvent *event);
+private:
+ void tryCreateObject();
+ void showObject(QObject *object);
+
+ QScopedPointer<QQuickItem> m_dummyItem;
+ QList<QQmlEngine *> m_engines;
+ QVector<QPointer<QObject>> m_createdObjects;
+ QScopedPointer<QQmlComponent> m_component;
+ QPointer<QQuickWindow> m_currentWindow;
+ bool m_supportsMultipleWindows;
+ QQmlPreviewPosition m_lastPosition;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLPREVIEWHANDLER_H
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp
new file mode 100644
index 0000000000..2382f72fe3
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlpreviewposition.h"
+
+#include <QtGui/qwindow.h>
+#include <QtGui/qscreen.h>
+#include <QtGui/qguiapplication.h>
+
+QT_BEGIN_NAMESPACE
+
+static const QSize availableScreenSize(const QPoint &point)
+{
+ if (const QScreen *screen = QGuiApplication::screenAt(point))
+ return screen->availableGeometry().size();
+ return QSize();
+}
+
+QQmlPreviewPosition::QQmlPreviewPosition()
+ : m_settings("QtProject", "QtQmlPreview")
+{
+ m_savePositionTimer.setSingleShot(true);
+ m_savePositionTimer.setInterval(500);
+ QObject::connect(&m_savePositionTimer, &QTimer::timeout, [this]() {
+ saveWindowPosition();
+ });
+}
+
+void QQmlPreviewPosition::setPosition(const QPoint &point)
+{
+ m_hasPosition = true;
+ m_lastWindowPosition = point;
+ m_savePositionTimer.start();
+}
+
+void QQmlPreviewPosition::saveWindowPosition()
+{
+ if (m_hasPosition) {
+ if (!m_settingsKey.isNull())
+ m_settings.setValue(m_settingsKey, m_lastWindowPosition);
+
+ m_settings.setValue(QLatin1String("global_lastpostion"), m_lastWindowPosition);
+ }
+}
+
+void QQmlPreviewPosition::loadWindowPositionSettings(const QUrl &url)
+{
+ m_settingsKey = url.toString(QUrl::PreferLocalFile) + QLatin1String("_lastpostion");
+
+ if (m_settings.contains(m_settingsKey)) {
+ m_hasPosition = true;
+ m_lastWindowPosition = m_settings.value(m_settingsKey).toPoint();
+ }
+}
+
+void QQmlPreviewPosition::initLastSavedWindowPosition(QWindow *window)
+{
+ if (m_positionedWindows.contains(window))
+ return;
+ if (!m_hasPosition) {
+ // in case there was nothing saved, we do not want to set anything
+ if (!m_settings.contains(QLatin1String("global_lastpostion")))
+ return;
+ m_lastWindowPosition = m_settings.value(QLatin1String("global_lastpostion")).toPoint();
+ }
+ if (QGuiApplication::screenAt(m_lastWindowPosition))
+ window->setFramePosition(m_lastWindowPosition);
+
+ m_positionedWindows.append(window);
+}
+
+const QSize QQmlPreviewPosition::currentScreenSize(QWindow *window)
+{
+ return availableScreenSize(window->position());
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.h
new file mode 100644
index 0000000000..8683757007
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLPREVIEWPOSITION_H
+#define QQMLPREVIEWPOSITION_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/qvector.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qsettings.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWindow;
+
+class QQmlPreviewPosition
+{
+public:
+ QQmlPreviewPosition();
+
+ void setPosition(const QPoint &point);
+ void saveWindowPosition();
+ void loadWindowPositionSettings(const QUrl &url);
+ void initLastSavedWindowPosition(QWindow *window);
+ static const QSize currentScreenSize(QWindow *window);
+
+private:
+ bool m_hasPosition = false;
+ QPoint m_lastWindowPosition;
+ QSettings m_settings;
+ QString m_settingsKey;
+ QTimer m_savePositionTimer;
+ QVector<QWindow *> m_positionedWindows;
+};
+
+
+QT_END_NAMESPACE
+
+#endif // QQMLPREVIEWPOSITION_H
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp
new file mode 100644
index 0000000000..4e96fc62ef
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp
@@ -0,0 +1,150 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlpreviewservice.h"
+
+#include <QtCore/qpointer.h>
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtQuick/qquickwindow.h>
+#include <QtQuick/qquickitem.h>
+#include <QtGui/qguiapplication.h>
+
+#include <private/qquickpixmapcache_p.h>
+#include <private/qqmldebugconnector_p.h>
+#include <private/qversionedpacket_p.h>
+
+QT_BEGIN_NAMESPACE
+
+const QString QQmlPreviewServiceImpl::s_key = QStringLiteral("QmlPreview");
+using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>;
+
+QQmlPreviewServiceImpl::QQmlPreviewServiceImpl(QObject *parent) :
+ QQmlDebugService(s_key, 1.0f, parent)
+{
+ m_loader.reset(new QQmlPreviewFileLoader(this));
+ connect(this, &QQmlPreviewServiceImpl::load,
+ m_loader.data(), &QQmlPreviewFileLoader::whitelist, Qt::DirectConnection);
+ connect(this, &QQmlPreviewServiceImpl::load, &m_handler, &QQmlPreviewHandler::loadUrl);
+ connect(this, &QQmlPreviewServiceImpl::rerun, &m_handler, &QQmlPreviewHandler::rerun);
+ connect(this, &QQmlPreviewServiceImpl::zoom, &m_handler, &QQmlPreviewHandler::zoom);
+ connect(&m_handler, &QQmlPreviewHandler::error, this, &QQmlPreviewServiceImpl::forwardError,
+ Qt::DirectConnection);
+
+}
+
+QQmlPreviewServiceImpl::~QQmlPreviewServiceImpl()
+{
+}
+
+void QQmlPreviewServiceImpl::messageReceived(const QByteArray &data)
+{
+ QQmlDebugPacket packet(data);
+ qint8 command;
+
+ packet >> command;
+ switch (command) {
+ case File: {
+ QString path;
+ QByteArray contents;
+ packet >> path >> contents;
+ emit file(path, contents);
+ break;
+ }
+ case Directory: {
+ QString path;
+ QStringList entries;
+ packet >> path >> entries;
+ emit directory(path, entries);
+ break;
+ }
+ case Load: {
+ QUrl url;
+ packet >> url;
+ emit load(url);
+ break;
+ }
+ case Error: {
+ QString file;
+ packet >> file;
+ emit error(file);
+ break;
+ }
+ case Rerun:
+ emit rerun();
+ break;
+ case ClearCache:
+ emit clearCache();
+ break;
+ case Zoom: {
+ float factor;
+ packet >> factor;
+ emit zoom(static_cast<qreal>(factor));
+ break;
+ }
+ default:
+ forwardError(QString::fromLatin1("Invalid command: %1").arg(command));
+ break;
+ }
+}
+
+void QQmlPreviewServiceImpl::engineAboutToBeAdded(QJSEngine *engine)
+{
+ if (QQmlEngine *qmlEngine = qobject_cast<QQmlEngine *>(engine))
+ m_handler.addEngine(qmlEngine);
+ emit attachedToEngine(engine);
+}
+
+void QQmlPreviewServiceImpl::engineAboutToBeRemoved(QJSEngine *engine)
+{
+ if (QQmlEngine *qmlEngine = qobject_cast<QQmlEngine *>(engine))
+ m_handler.removeEngine(qmlEngine);
+ emit detachedFromEngine(engine);
+}
+
+void QQmlPreviewServiceImpl::stateChanged(QQmlDebugService::State state)
+{
+ m_fileEngine.reset(state == Enabled ? new QQmlPreviewFileEngineHandler(m_loader.data())
+ : nullptr);
+}
+
+void QQmlPreviewServiceImpl::forwardRequest(const QString &file)
+{
+ QQmlDebugPacket packet;
+ packet << static_cast<qint8>(Request) << file;
+ emit messageToClient(name(), packet.data());
+}
+
+void QQmlPreviewServiceImpl::forwardError(const QString &error)
+{
+ QQmlDebugPacket packet;
+ packet << static_cast<qint8>(Error) << error;
+ emit messageToClient(name(), packet.data());
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h
new file mode 100644
index 0000000000..084d3a14b1
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of th QML Preview debug service.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLPREVIEWSERVICE_H
+#define QQMLPREVIEWSERVICE_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 "qqmlpreviewhandler.h"
+#include "qqmlpreviewfileengine.h"
+#include <private/qqmldebugserviceinterfaces_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlPreviewFileEngineHandler;
+class QQmlPreviewHandler;
+class QQmlPreviewServiceImpl : public QQmlDebugService
+{
+ Q_OBJECT
+
+public:
+ enum Command {
+ File,
+ Load,
+ Request,
+ Error,
+ Rerun,
+ Directory,
+ ClearCache,
+ Zoom
+ };
+
+ static const QString s_key;
+
+ QQmlPreviewServiceImpl(QObject *parent = nullptr);
+ virtual ~QQmlPreviewServiceImpl();
+
+ void messageReceived(const QByteArray &message) override;
+ void engineAboutToBeAdded(QJSEngine *engine) override;
+ void engineAboutToBeRemoved(QJSEngine *engine) override;
+ void stateChanged(State state) override;
+
+ void forwardRequest(const QString &file);
+ void forwardError(const QString &error);
+
+signals:
+ void error(const QString &file);
+ void file(const QString &file, const QByteArray &contents);
+ void directory(const QString &file, const QStringList &entries);
+ void load(const QUrl &url);
+ void rerun();
+ void clearCache();
+ void zoom(qreal factor);
+
+private:
+ QScopedPointer<QQmlPreviewFileEngineHandler> m_fileEngine;
+ QScopedPointer<QQmlPreviewFileLoader> m_loader;
+ QQmlPreviewHandler m_handler;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLPREVIEWSERVICE_H
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.json b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.json
new file mode 100644
index 0000000000..d7e1ef1f10
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.json
@@ -0,0 +1,3 @@
+{
+ "Keys" : [ "QmlPreview" ]
+}
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.cpp
new file mode 100644
index 0000000000..4e09bd1002
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.cpp
@@ -0,0 +1,39 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlpreviewservicefactory.h"
+#include "qqmlpreviewservice.h"
+
+QT_BEGIN_NAMESPACE
+
+QQmlDebugService *QQmlPreviewServiceFactory::create(const QString &key)
+{
+ return key == QQmlPreviewServiceImpl::s_key ? new QQmlPreviewServiceImpl(this) : nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.h
new file mode 100644
index 0000000000..f51e696fe6
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLPREVIEWSERVCIEFACTORY_H
+#define QQMLPREVIEWSERVCIEFACTORY_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 <private/qqmldebugservicefactory_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlPreviewServiceFactory : public QQmlDebugServiceFactory
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlDebugServiceFactory_iid FILE "qqmlpreviewservice.json")
+
+public:
+ QQmlDebugService *create(const QString &key) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLPREVIEWSERVCIEFACTORY_H
diff --git a/src/plugins/qmltooling/qmltooling.pro b/src/plugins/qmltooling/qmltooling.pro
index 119415372b..fd4a3eac65 100644
--- a/src/plugins/qmltooling/qmltooling.pro
+++ b/src/plugins/qmltooling/qmltooling.pro
@@ -35,7 +35,10 @@ qmldbg_nativedebugger.depends = packetprotocol
qtHaveModule(quick) {
SUBDIRS += \
qmldbg_inspector \
- qmldbg_quickprofiler
+ qmldbg_quickprofiler \
+ qmldbg_preview
+
qmldbg_inspector.depends = packetprotocol
qmldbg_quickprofiler.depends = packetprotocol
+ qmldbg_preview.depends = packetprotocol
}