1 | // Copyright (C) 2018 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qqmlpreviewservice.h" |
5 | |
6 | #include <QtCore/qpointer.h> |
7 | #include <QtQml/qqmlengine.h> |
8 | #include <QtQml/qqmlcomponent.h> |
9 | #include <QtQuick/qquickwindow.h> |
10 | #include <QtQuick/qquickitem.h> |
11 | #include <QtGui/qguiapplication.h> |
12 | |
13 | #include <private/qquickpixmapcache_p.h> |
14 | #include <private/qqmldebugconnector_p.h> |
15 | #include <private/qversionedpacket_p.h> |
16 | |
17 | QT_BEGIN_NAMESPACE |
18 | |
19 | const QString QQmlPreviewServiceImpl::s_key = QStringLiteral("QmlPreview" ); |
20 | using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>; |
21 | |
22 | QQmlPreviewServiceImpl::QQmlPreviewServiceImpl(QObject *parent) : |
23 | QQmlDebugService(s_key, 1.0f, parent) |
24 | { |
25 | m_loader.reset(other: new QQmlPreviewFileLoader(this)); |
26 | connect(sender: this, signal: &QQmlPreviewServiceImpl::load, |
27 | context: m_loader.data(), slot: &QQmlPreviewFileLoader::whitelist, type: Qt::DirectConnection); |
28 | connect(sender: this, signal: &QQmlPreviewServiceImpl::load, context: &m_handler, slot: &QQmlPreviewHandler::loadUrl); |
29 | connect(sender: this, signal: &QQmlPreviewServiceImpl::rerun, context: &m_handler, slot: &QQmlPreviewHandler::rerun); |
30 | connect(sender: this, signal: &QQmlPreviewServiceImpl::zoom, context: &m_handler, slot: &QQmlPreviewHandler::zoom); |
31 | connect(sender: &m_handler, signal: &QQmlPreviewHandler::error, context: this, slot: &QQmlPreviewServiceImpl::forwardError, |
32 | type: Qt::DirectConnection); |
33 | connect(sender: &m_handler, signal: &QQmlPreviewHandler::fps, context: this, slot: &QQmlPreviewServiceImpl::forwardFps, |
34 | type: Qt::DirectConnection); |
35 | } |
36 | |
37 | QQmlPreviewServiceImpl::~QQmlPreviewServiceImpl() |
38 | { |
39 | } |
40 | |
41 | void QQmlPreviewServiceImpl::messageReceived(const QByteArray &data) |
42 | { |
43 | QQmlDebugPacket packet(data); |
44 | qint8 command; |
45 | |
46 | packet >> command; |
47 | switch (command) { |
48 | case File: { |
49 | QString path; |
50 | QByteArray contents; |
51 | packet >> path >> contents; |
52 | emit file(file: path, contents); |
53 | |
54 | // Replace the whole scene with the first file successfully loaded over the debug |
55 | // connection. This is an OK approximation of the root component, and if the client wants |
56 | // something specific, it will send an explicit Load anyway. |
57 | if (m_currentUrl.isEmpty() && path.endsWith(s: ".qml" )) { |
58 | if (path.startsWith(c: ':')) |
59 | m_currentUrl = QUrl("qrc" + path); |
60 | else |
61 | m_currentUrl = QUrl::fromLocalFile(localfile: path); |
62 | emit load(url: m_currentUrl); |
63 | } |
64 | break; |
65 | } |
66 | case Directory: { |
67 | QString path; |
68 | QStringList entries; |
69 | packet >> path >> entries; |
70 | emit directory(file: path, entries); |
71 | break; |
72 | } |
73 | case Load: { |
74 | QUrl url; |
75 | packet >> url; |
76 | if (url.isEmpty()) |
77 | url = m_currentUrl; |
78 | else |
79 | m_currentUrl = url; |
80 | emit load(url); |
81 | break; |
82 | } |
83 | case Error: { |
84 | QString file; |
85 | packet >> file; |
86 | emit error(file); |
87 | break; |
88 | } |
89 | case Rerun: |
90 | emit rerun(); |
91 | break; |
92 | case ClearCache: |
93 | emit clearCache(); |
94 | break; |
95 | case Zoom: { |
96 | float factor; |
97 | packet >> factor; |
98 | emit zoom(factor: static_cast<qreal>(factor)); |
99 | break; |
100 | } |
101 | default: |
102 | forwardError(error: QString::fromLatin1(ba: "Invalid command: %1" ).arg(a: command)); |
103 | break; |
104 | } |
105 | } |
106 | |
107 | void QQmlPreviewServiceImpl::engineAboutToBeAdded(QJSEngine *engine) |
108 | { |
109 | if (QQmlEngine *qmlEngine = qobject_cast<QQmlEngine *>(object: engine)) |
110 | m_handler.addEngine(engine: qmlEngine); |
111 | emit attachedToEngine(engine); |
112 | } |
113 | |
114 | void QQmlPreviewServiceImpl::engineAboutToBeRemoved(QJSEngine *engine) |
115 | { |
116 | if (QQmlEngine *qmlEngine = qobject_cast<QQmlEngine *>(object: engine)) |
117 | m_handler.removeEngine(engine: qmlEngine); |
118 | emit detachedFromEngine(engine); |
119 | } |
120 | |
121 | void QQmlPreviewServiceImpl::stateChanged(QQmlDebugService::State state) |
122 | { |
123 | m_fileEngine.reset(other: state == Enabled ? new QQmlPreviewFileEngineHandler(m_loader.data()) |
124 | : nullptr); |
125 | } |
126 | |
127 | void QQmlPreviewServiceImpl::forwardRequest(const QString &file) |
128 | { |
129 | QQmlDebugPacket packet; |
130 | packet << static_cast<qint8>(Request) << file; |
131 | emit messageToClient(name: name(), message: packet.data()); |
132 | } |
133 | |
134 | void QQmlPreviewServiceImpl::forwardError(const QString &error) |
135 | { |
136 | QQmlDebugPacket packet; |
137 | packet << static_cast<qint8>(Error) << error; |
138 | emit messageToClient(name: name(), message: packet.data()); |
139 | } |
140 | |
141 | void QQmlPreviewServiceImpl::forwardFps(const QQmlPreviewHandler::FpsInfo &frames) |
142 | { |
143 | QQmlDebugPacket packet; |
144 | packet << static_cast<qint8>(Fps) |
145 | << frames.numSyncs << frames.minSync << frames.maxSync << frames.totalSync |
146 | << frames.numRenders << frames.minRender << frames.maxRender << frames.totalRender; |
147 | emit messageToClient(name: name(), message: packet.data()); |
148 | } |
149 | |
150 | QQuickItem *QQmlPreviewServiceImpl::currentRootItem() |
151 | { |
152 | return m_handler.currentRootItem(); |
153 | } |
154 | |
155 | QT_END_NAMESPACE |
156 | |
157 | #include "moc_qqmlpreviewservice.cpp" |
158 | |