aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarco Bubke <marco.bubke@qt.io>2020-09-16 13:44:43 +0200
committerTim Jenssen <tim.jenssen@qt.io>2020-10-16 10:01:21 +0000
commitd1b0c12d6b6c4698492851716b3931bc9cae5fd3 (patch)
tree9aa7d8fddbafced065e392b84939c59d6ae8726e
parent58e612c85fb8320fd99a28d573372220cbfe309a (diff)
QmlDesigner: Add image cache
The image cache is saving images and icon of this images in a sqlite database. If there are no images they are generated in the backgound. The icons are fetched by item library. Task-number: QDS-2782 Task-number: QDS-2783 Task-number: QDS-2858 Change-Id: I5a32cccfef7f8fd8eb78902605a09f5da18ce88e Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
-rw-r--r--share/qtcreator/qml/qmlpuppet/commands/captureddatacommand.h13
-rw-r--r--share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.h1
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/instances.pri5
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp3
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp84
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.h45
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturepreviewnodeinstanceserver.cpp2
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp4
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp5
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppetmain.cpp5
-rw-r--r--share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml7
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImagePreviewTooltipArea.qml48
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir1
-rw-r--r--src/libs/sqlite/CMakeLists.txt2
-rw-r--r--src/libs/sqlite/sqlite-lib.pri2
-rw-r--r--src/libs/sqlite/sqlitebasestatement.cpp12
-rw-r--r--src/libs/sqlite/sqlitebasestatement.h7
-rw-r--r--src/libs/sqlite/sqliteblob.h100
-rw-r--r--src/libs/sqlite/sqlitesessionchangeset.cpp4
-rw-r--r--src/libs/sqlite/sqlitesessionchangeset.h7
-rw-r--r--src/libs/sqlite/sqlitesessions.cpp2
-rw-r--r--src/libs/sqlite/sqlitetimestamp.h47
-rw-r--r--src/libs/sqlite/sqlitevalue.h5
-rw-r--r--src/plugins/projectexplorer/target.h6
-rw-r--r--src/plugins/qmldesigner/CMakeLists.txt19
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrary.pri2
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp1
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp93
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.h58
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.cpp14
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.h2
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp2
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp51
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h2
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp64
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h11
-rw-r--r--src/plugins/qmldesigner/components/navigator/previewtooltip.ui9
-rw-r--r--src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp63
-rw-r--r--src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.h53
-rw-r--r--src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.ui158
-rw-r--r--src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp110
-rw-r--r--src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.h71
-rw-r--r--src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.pri11
-rw-r--r--src/plugins/qmldesigner/designercore/designercore-lib.pri21
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecache.cpp166
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp117
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h66
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h48
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecacheconnectionmanager.cpp68
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecacheconnectionmanager.h45
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp159
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h95
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h53
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h197
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachestorageinterface.h58
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/timestampprovider.cpp38
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/timestampprovider.h39
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/timestampproviderinterface.h44
-rw-r--r--src/plugins/qmldesigner/designercore/include/basetexteditmodifier.h3
-rw-r--r--src/plugins/qmldesigner/designercore/include/filesystemfacadeinterface.h37
-rw-r--r--src/plugins/qmldesigner/designercore/include/imagecache.h113
-rw-r--r--src/plugins/qmldesigner/designercore/include/nodeinstanceview.h6
-rw-r--r--src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h20
-rw-r--r--src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp25
-rw-r--r--src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.h24
-rw-r--r--src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.cpp9
-rw-r--r--src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.h5
-rw-r--r--src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp20
-rw-r--r--src/plugins/qmldesigner/designercore/instances/connectionmanager.h12
-rw-r--r--src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.h10
-rw-r--r--src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.cpp27
-rw-r--r--src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.h10
-rw-r--r--src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp2
-rw-r--r--src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h5
-rw-r--r--src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp12
-rw-r--r--src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp6
-rw-r--r--src/plugins/qmldesigner/designercore/instances/puppetcreator.h2
-rw-r--r--src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp42
-rw-r--r--src/plugins/qmldesigner/designercore/model/plaintexteditmodifier.cpp24
-rw-r--r--src/plugins/qmldesigner/qmldesigner_dependencies.pri3
-rw-r--r--src/plugins/qmldesigner/qmldesignerplugin.pro1
-rw-r--r--src/plugins/qmldesigner/qmldesignerplugin.qbs18
-rw-r--r--src/plugins/qmldesigner/qmldesignerunittestfiles.pri12
-rw-r--r--src/tools/qml2puppet/CMakeLists.txt1
-rw-r--r--src/tools/qml2puppet/qml2puppet.qbs2
-rw-r--r--tests/unit/mockup/qmldesigner/designercore/include/nodeinstanceview.h6
-rw-r--r--tests/unit/unittest/CMakeLists.txt21
-rw-r--r--tests/unit/unittest/gmock_dependency.pri6
-rw-r--r--tests/unit/unittest/google-using-declarations.h4
-rw-r--r--tests/unit/unittest/gtest-creator-printing.cpp10
-rw-r--r--tests/unit/unittest/gtest-creator-printing.h7
-rw-r--r--tests/unit/unittest/gtest-qt-printing.cpp5
-rw-r--r--tests/unit/unittest/gtest-qt-printing.h2
-rw-r--r--tests/unit/unittest/imagecache-test.cpp326
-rw-r--r--tests/unit/unittest/imagecachecollectormock.h41
-rw-r--r--tests/unit/unittest/imagecachegenerator-test.cpp240
-rw-r--r--tests/unit/unittest/imagecachestorage-test.cpp334
-rw-r--r--tests/unit/unittest/mockimagecachegenerator.h43
-rw-r--r--tests/unit/unittest/mockimagecachestorage.h50
-rw-r--r--tests/unit/unittest/mocktimestampprovider.h36
-rw-r--r--tests/unit/unittest/notification.h56
-rw-r--r--tests/unit/unittest/sqlitedatabasemock.h72
-rw-r--r--tests/unit/unittest/sqlitereadstatementmock.cpp60
-rw-r--r--tests/unit/unittest/sqlitereadstatementmock.h93
-rw-r--r--tests/unit/unittest/sqlitestatement-test.cpp50
-rw-r--r--tests/unit/unittest/sqlitestatementmock.h105
-rw-r--r--tests/unit/unittest/sqlitetransactionbackendmock.h46
-rw-r--r--tests/unit/unittest/sqlitewritestatementmock.cpp35
-rw-r--r--tests/unit/unittest/sqlitewritestatementmock.h84
-rw-r--r--tests/unit/unittest/unittest.pro19
-rw-r--r--tests/unit/unittest/unittest.qbs13
111 files changed, 4403 insertions, 237 deletions
diff --git a/share/qtcreator/qml/qmlpuppet/commands/captureddatacommand.h b/share/qtcreator/qml/qmlpuppet/commands/captureddatacommand.h
index 6c3715dd5f..0d3bbba7b7 100644
--- a/share/qtcreator/qml/qmlpuppet/commands/captureddatacommand.h
+++ b/share/qtcreator/qml/qmlpuppet/commands/captureddatacommand.h
@@ -151,8 +151,19 @@ public:
qint32 nodeId = -1;
};
+ CapturedDataCommand() = default;
+
+ CapturedDataCommand(QVector<StateData> &&stateData)
+ : stateData{std::move(stateData)}
+ {}
+
+ CapturedDataCommand(QImage &&image)
+ : image{std::move(image)}
+ {}
+
friend QDataStream &operator<<(QDataStream &out, const CapturedDataCommand &command)
{
+ out << command.image;
out << command.stateData;
return out;
@@ -160,12 +171,14 @@ public:
friend QDataStream &operator>>(QDataStream &in, CapturedDataCommand &command)
{
+ in >> command.image;
in >> command.stateData;
return in;
}
public:
+ QImage image;
QVector<StateData> stateData;
};
diff --git a/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.h b/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.h
index 841cd8561e..b4ffb74903 100644
--- a/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.h
+++ b/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.h
@@ -86,6 +86,7 @@ public:
virtual void requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command) = 0;
virtual void changeLanguage(const ChangeLanguageCommand &command) = 0;
virtual void changePreviewImageSize(const ChangePreviewImageSizeCommand &command) = 0;
+ virtual void dispatchCommand(const QVariant &) {}
virtual void benchmark(const QString &) {}
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/instances.pri b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/instances.pri
index 85de00c5ff..448a67c913 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/instances.pri
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/instances.pri
@@ -9,6 +9,7 @@ HEADERS += $$PWD/qt5nodeinstanceserver.h \
$$PWD/capturenodeinstanceserverdispatcher.h \
$$PWD/capturescenecreatedcommand.h \
$$PWD/nodeinstanceserverdispatcher.h \
+ $$PWD/qt5captureimagenodeinstanceserver.h \
$$PWD/qt5capturepreviewnodeinstanceserver.h \
$$PWD/qt5testnodeinstanceserver.h \
$$PWD/qt5informationnodeinstanceserver.h \
@@ -33,11 +34,13 @@ HEADERS += $$PWD/qt5nodeinstanceserver.h \
$$PWD/layoutnodeinstance.h \
$$PWD/qt3dpresentationnodeinstance.h \
$$PWD/quick3dnodeinstance.h \
- $$PWD/quick3dtexturenodeinstance.h
+ $$PWD/quick3dtexturenodeinstance.h \
+
SOURCES += $$PWD/qt5nodeinstanceserver.cpp \
$$PWD/capturenodeinstanceserverdispatcher.cpp \
$$PWD/nodeinstanceserverdispatcher.cpp \
+ $$PWD/qt5captureimagenodeinstanceserver.cpp \
$$PWD/qt5capturepreviewnodeinstanceserver.cpp \
$$PWD/qt5testnodeinstanceserver.cpp \
$$PWD/qt5informationnodeinstanceserver.cpp \
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp
index dd9c42c5bd..eb5aae6a7e 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp
@@ -25,6 +25,7 @@
#include "nodeinstanceserverdispatcher.h"
+#include "qt5captureimagenodeinstanceserver.h"
#include "qt5capturepreviewnodeinstanceserver.h"
#include "qt5informationnodeinstanceserver.h"
#include "qt5rendernodeinstanceserver.h"
@@ -183,6 +184,8 @@ std::unique_ptr<NodeInstanceServer> createNodeInstanceServer(
{
if (serverName == "capturemode")
return std::make_unique<Qt5CapturePreviewNodeInstanceServer>(nodeInstanceClient);
+ else if (serverName == "captureiconmode")
+ return std::make_unique<Qt5CaptureImageNodeInstanceServer>(nodeInstanceClient);
else if (serverName == "rendermode")
return std::make_unique<Qt5RenderNodeInstanceServer>(nodeInstanceClient);
else if (serverName == "editormode")
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp
new file mode 100644
index 0000000000..d24c4e5552
--- /dev/null
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#include "qt5captureimagenodeinstanceserver.h"
+#include "servernodeinstance.h"
+
+#include <captureddatacommand.h>
+#include <createscenecommand.h>
+#include <nodeinstanceclientinterface.h>
+
+#include <QImage>
+#include <QQuickItem>
+#include <QQuickView>
+
+namespace QmlDesigner {
+
+namespace {
+
+QImage renderImage(ServerNodeInstance rootNodeInstance)
+{
+ rootNodeInstance.updateDirtyNodeRecursive();
+
+ QSize previewImageSize = rootNodeInstance.boundingRect().size().toSize();
+ if (previewImageSize.isEmpty())
+ previewImageSize = {640, 480};
+
+ if (previewImageSize.width() > 800 || previewImageSize.height() > 800)
+ previewImageSize.scale({800, 800}, Qt::KeepAspectRatio);
+
+ QImage previewImage = rootNodeInstance.renderPreviewImage(previewImageSize);
+
+ return previewImage;
+}
+} // namespace
+
+void Qt5CaptureImageNodeInstanceServer::collectItemChangesAndSendChangeCommands()
+{
+ static bool inFunction = false;
+
+ if (!rootNodeInstance().holdsGraphical()) {
+ nodeInstanceClient()->capturedData(CapturedDataCommand{});
+ return;
+ }
+
+ if (!inFunction) {
+ inFunction = true;
+
+ auto rooNodeInstance = rootNodeInstance();
+ rooNodeInstance.rootQuickItem()->setClip(true);
+
+ DesignerSupport::polishItems(quickView());
+
+ QImage image = renderImage(rooNodeInstance);
+
+ nodeInstanceClient()->capturedData(CapturedDataCommand{std::move(image)});
+
+ slowDownRenderTimer();
+ inFunction = false;
+ }
+}
+
+} // namespace
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.h
new file mode 100644
index 0000000000..7c26e47a87
--- /dev/null
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.h
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <qt5previewnodeinstanceserver.h>
+
+namespace QmlDesigner {
+
+class Qt5CaptureImageNodeInstanceServer : public Qt5PreviewNodeInstanceServer
+{
+public:
+ explicit Qt5CaptureImageNodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient)
+ : Qt5PreviewNodeInstanceServer(nodeInstanceClient)
+ {}
+
+protected:
+ void collectItemChangesAndSendChangeCommands() override;
+
+private:
+};
+
+} // namespace QmlDesigner
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturepreviewnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturepreviewnodeinstanceserver.cpp
index c70ef76afe..7fb87defb0 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturepreviewnodeinstanceserver.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturepreviewnodeinstanceserver.cpp
@@ -100,7 +100,7 @@ void Qt5CapturePreviewNodeInstanceServer::collectItemChangesAndSendChangeCommand
stateInstance.deactivateState();
}
- nodeInstanceClient()->capturedData(CapturedDataCommand{stateDatas});
+ nodeInstanceClient()->capturedData(CapturedDataCommand{std::move(stateDatas)});
slowDownRenderTimer();
inFunction = false;
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp
index 64b6eb409c..93e6e786cd 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp
@@ -28,6 +28,7 @@
#include <QCoreApplication>
#include "capturenodeinstanceserverdispatcher.h"
+#include "qt5captureimagenodeinstanceserver.h"
#include "qt5capturepreviewnodeinstanceserver.h"
#include "qt5informationnodeinstanceserver.h"
#include "qt5previewnodeinstanceserver.h"
@@ -92,6 +93,9 @@ Qt5NodeInstanceClientProxy::Qt5NodeInstanceClientProxy(QObject *parent) :
} else if (QCoreApplication::arguments().at(2) == QLatin1String("capturemode")) {
setNodeInstanceServer(std::make_unique<Qt5CapturePreviewNodeInstanceServer>(this));
initializeSocket();
+ } else if (QCoreApplication::arguments().at(2) == QLatin1String("captureiconmode")) {
+ setNodeInstanceServer(std::make_unique<Qt5CaptureImageNodeInstanceServer>(this));
+ initializeSocket();
}
}
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp
index bfaff0eea8..1f54bffdbd 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp
@@ -566,7 +566,7 @@ QRectF QuickItemNodeInstance::boundingRectWithStepChilds(QQuickItem *parentItem)
boundingRect = boundingRect.united(QRectF(QPointF(0, 0), size()));
- foreach (QQuickItem *childItem, parentItem->childItems()) {
+ for (QQuickItem *childItem : parentItem->childItems()) {
if (!nodeInstanceServer()->hasInstanceForObject(childItem)) {
QRectF transformedRect = childItem->mapRectToItem(parentItem, boundingRectWithStepChilds(childItem));
if (isRectangleSane(transformedRect))
@@ -574,6 +574,9 @@ QRectF QuickItemNodeInstance::boundingRectWithStepChilds(QQuickItem *parentItem)
}
}
+ if (boundingRect.isEmpty())
+ QRectF{0, 0, 640, 480};
+
return boundingRect;
}
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppetmain.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppetmain.cpp
index 98df3de295..1f94bc3e59 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppetmain.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppetmain.cpp
@@ -42,9 +42,11 @@
#endif
#ifdef Q_OS_WIN
-#include <windows.h>
+#include <Windows.h>
#endif
+namespace {
+
int internalMain(QGuiApplication *application)
{
QCoreApplication::setOrganizationName("QtProject");
@@ -138,6 +140,7 @@ int internalMain(QGuiApplication *application)
return application->exec();
}
+} // namespace
int main(int argc, char *argv[])
{
diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml
index 1cbec5d802..b7bc9b0b58 100644
--- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml
+++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml
@@ -46,8 +46,6 @@ Item {
width: itemLibraryIconWidth // to be set in Qml context
height: itemLibraryIconHeight // to be set in Qml context
source: itemLibraryIconPath // to be set by model
-
- cache: false // Allow thumbnail to be dynamically updated
}
Text {
@@ -71,10 +69,11 @@ Item {
renderType: Text.NativeRendering
}
- ToolTipArea {
+ ImagePreviewTooltipArea {
id: mouseRegion
+
anchors.fill: parent
- tooltip: itemName
+
onPressed: {
rootView.startDragAndDrop(mouseRegion, itemLibraryEntry)
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImagePreviewTooltipArea.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImagePreviewTooltipArea.qml
new file mode 100644
index 0000000000..51194cc3cb
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImagePreviewTooltipArea.qml
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+import QtQuick 2.1
+import HelperWidgets 2.0
+import QtQuick.Layouts 1.0
+
+MouseArea {
+ id: mouseArea
+
+ onExited: tooltipBackend.hideTooltip()
+ onCanceled: tooltipBackend.hideTooltip()
+ onClicked: forceActiveFocus()
+
+ hoverEnabled: true
+
+ Timer {
+ interval: 1000
+ running: mouseArea.containsMouse
+ onTriggered: {
+ tooltipBackend.componentName = itemName
+ tooltipBackend.componentPath = componentPath
+ tooltipBackend.showTooltip()
+ }
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir
index 59573217c5..0da6489551 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir
@@ -47,3 +47,4 @@ ExpressionTextField 2.0 ExpressionTextField.qml
MarginSection 2.0 MarginSection.qml
HorizontalScrollBar 2.0 HorizontalScrollBar.qml
VerticalScrollBar 2.0 VerticalScrollBar.qml
+ImagePreviewTooltipArea 2.0 ImagePreviewTooltipArea.qml
diff --git a/src/libs/sqlite/CMakeLists.txt b/src/libs/sqlite/CMakeLists.txt
index 30e5edf12f..f684fa0849 100644
--- a/src/libs/sqlite/CMakeLists.txt
+++ b/src/libs/sqlite/CMakeLists.txt
@@ -44,4 +44,6 @@ add_qtc_library(Sqlite
tableconstraints.h
utf8string.cpp utf8string.h
utf8stringvector.cpp utf8stringvector.h
+ sqliteblob.h
+ sqlitetimestamp.h
)
diff --git a/src/libs/sqlite/sqlite-lib.pri b/src/libs/sqlite/sqlite-lib.pri
index 8ded8ca106..fd02c8801f 100644
--- a/src/libs/sqlite/sqlite-lib.pri
+++ b/src/libs/sqlite/sqlite-lib.pri
@@ -27,6 +27,8 @@ SOURCES += \
$$PWD/sqlitebasestatement.cpp
HEADERS += \
$$PWD/constraints.h \
+ $$PWD/sqliteblob.h \
+ $$PWD/sqlitetimestamp.h \
$$PWD/tableconstraints.h \
$$PWD/createtablesqlstatementbuilder.h \
$$PWD/lastchangedrowid.h \
diff --git a/src/libs/sqlite/sqlitebasestatement.cpp b/src/libs/sqlite/sqlitebasestatement.cpp
index dc4dd465e7..1bd7ef62a2 100644
--- a/src/libs/sqlite/sqlitebasestatement.cpp
+++ b/src/libs/sqlite/sqlitebasestatement.cpp
@@ -191,12 +191,12 @@ void BaseStatement::bind(int index, Utils::SmallStringView text)
checkForBindingError(resultCode);
}
-void BaseStatement::bind(int index, Utils::span<const byte> bytes)
+void BaseStatement::bind(int index, BlobView blobView)
{
int resultCode = sqlite3_bind_blob64(m_compiledStatement.get(),
index,
- bytes.data(),
- static_cast<long long>(bytes.size()),
+ blobView.data(),
+ blobView.size(),
SQLITE_STATIC);
if (resultCode != SQLITE_OK)
checkForBindingError(resultCode);
@@ -498,7 +498,7 @@ StringType textForColumn(sqlite3_stmt *sqlStatment, int column)
return StringType(text, size);
}
-Utils::span<const byte> blobForColumn(sqlite3_stmt *sqlStatment, int column)
+BlobView blobForColumn(sqlite3_stmt *sqlStatment, int column)
{
const byte *blob = reinterpret_cast<const byte *>(sqlite3_column_blob(sqlStatment, column));
std::size_t size = std::size_t(sqlite3_column_bytes(sqlStatment, column));
@@ -506,7 +506,7 @@ Utils::span<const byte> blobForColumn(sqlite3_stmt *sqlStatment, int column)
return {blob, size};
}
-Utils::span<const byte> convertToBlobForColumn(sqlite3_stmt *sqlStatment, int column)
+BlobView convertToBlobForColumn(sqlite3_stmt *sqlStatment, int column)
{
int dataType = sqlite3_column_type(sqlStatment, column);
if (dataType == SQLITE_BLOB)
@@ -571,7 +571,7 @@ double BaseStatement::fetchDoubleValue(int column) const
return sqlite3_column_double(m_compiledStatement.get(), column);
}
-Utils::span<const byte> BaseStatement::fetchBlobValue(int column) const
+BlobView BaseStatement::fetchBlobValue(int column) const
{
return convertToBlobForColumn(m_compiledStatement.get(), column);
}
diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h
index bae41665d5..0b990f7d93 100644
--- a/src/libs/sqlite/sqlitebasestatement.h
+++ b/src/libs/sqlite/sqlitebasestatement.h
@@ -27,6 +27,7 @@
#include "sqliteglobal.h"
+#include "sqliteblob.h"
#include "sqliteexception.h"
#include "sqlitevalue.h"
@@ -70,7 +71,7 @@ public:
double fetchDoubleValue(int column) const;
Utils::SmallStringView fetchSmallStringViewValue(int column) const;
ValueView fetchValueView(int column) const;
- Utils::span<const byte> fetchBlobValue(int column) const;
+ BlobView fetchBlobValue(int column) const;
template<typename Type>
Type fetchValue(int column) const;
int columnCount() const;
@@ -82,7 +83,7 @@ public:
void bind(int index, void *pointer);
void bind(int index, Utils::SmallStringView fetchValue);
void bind(int index, const Value &fetchValue);
- void bind(int index, Utils::span<const byte> bytes);
+ void bind(int index, BlobView blobView);
void bind(int index, uint value) { bind(index, static_cast<long long>(value)); }
@@ -358,7 +359,7 @@ private:
operator long long() { return statement.fetchLongLongValue(column); }
operator double() { return statement.fetchDoubleValue(column); }
operator Utils::SmallStringView() { return statement.fetchSmallStringViewValue(column); }
- operator Utils::span<const Sqlite::byte>() { return statement.fetchBlobValue(column); }
+ operator BlobView() { return statement.fetchBlobValue(column); }
operator ValueView() { return statement.fetchValueView(column); }
StatementImplementation &statement;
diff --git a/src/libs/sqlite/sqliteblob.h b/src/libs/sqlite/sqliteblob.h
new file mode 100644
index 0000000000..17d9426ad0
--- /dev/null
+++ b/src/libs/sqlite/sqliteblob.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "sqliteglobal.h"
+
+#include <utils/span.h>
+
+#include <QByteArray>
+
+#include <algorithm>
+#include <cstring>
+#include <vector>
+
+namespace Sqlite {
+
+class BlobView
+{
+public:
+ BlobView() = default;
+
+ BlobView(const byte *data, std::size_t size)
+ : m_data(data)
+ , m_size(size)
+ {}
+
+ BlobView(const QByteArray &byteArray)
+ : m_data(reinterpret_cast<const byte *>(byteArray.constData()))
+ , m_size(static_cast<std::size_t>(byteArray.size()))
+ {}
+
+ BlobView(const std::vector<Sqlite::byte> &bytes)
+ : m_data(bytes.data())
+ , m_size(static_cast<std::size_t>(bytes.size()))
+ {}
+
+ const byte *data() const { return m_data; }
+ const char *cdata() const { return reinterpret_cast<const char *>(m_data); }
+ std::size_t size() const { return m_size; }
+ int sisize() const { return static_cast<int>(m_size); }
+ long long ssize() const { return static_cast<long long>(m_size); }
+ bool empty() const { return !m_size; }
+
+ friend bool operator==(Sqlite::BlobView first, Sqlite::BlobView second)
+ {
+ return first.size() == second.size()
+ && std::memcmp(first.data(), second.data(), first.size()) == 0;
+ }
+
+private:
+ const byte *m_data{};
+ std::size_t m_size{};
+};
+
+class Blob
+{
+public:
+ Blob(BlobView blobView)
+ {
+ bytes.reserve(blobView.size());
+ std::copy_n(blobView.data(), blobView.size(), std::back_inserter(bytes));
+ }
+
+ std::vector<Sqlite::byte> bytes;
+};
+
+class ByteArrayBlob
+{
+public:
+ ByteArrayBlob(BlobView blobView)
+ : byteArray{blobView.cdata(), blobView.sisize()}
+ {}
+
+ QByteArray byteArray;
+};
+
+} // namespace Sqlite
diff --git a/src/libs/sqlite/sqlitesessionchangeset.cpp b/src/libs/sqlite/sqlitesessionchangeset.cpp
index 3aa43c625f..430cbae222 100644
--- a/src/libs/sqlite/sqlitesessionchangeset.cpp
+++ b/src/libs/sqlite/sqlitesessionchangeset.cpp
@@ -46,7 +46,7 @@ void checkResultCode(int resultCode)
} // namespace
-SessionChangeSet::SessionChangeSet(Utils::span<const byte> blob)
+SessionChangeSet::SessionChangeSet(BlobView blob)
: data(sqlite3_malloc64(blob.size()))
, size(int(blob.size()))
{
@@ -64,7 +64,7 @@ SessionChangeSet::~SessionChangeSet()
sqlite3_free(data);
}
-Utils::span<const byte> SessionChangeSet::asSpan() const
+BlobView SessionChangeSet::asBlobView() const
{
return {static_cast<const byte *>(data), static_cast<std::size_t>(size)};
}
diff --git a/src/libs/sqlite/sqlitesessionchangeset.h b/src/libs/sqlite/sqlitesessionchangeset.h
index 65396622a9..8ea2dba008 100644
--- a/src/libs/sqlite/sqlitesessionchangeset.h
+++ b/src/libs/sqlite/sqlitesessionchangeset.h
@@ -25,10 +25,9 @@
#pragma once
+#include "sqliteblob.h"
#include "sqliteglobal.h"
-#include <utils/span.h>
-
#include <memory>
#include <vector>
@@ -41,7 +40,7 @@ class Sessions;
class SessionChangeSet
{
public:
- SessionChangeSet(Utils::span<const byte> blob);
+ SessionChangeSet(BlobView blob);
SessionChangeSet(Sessions &session);
~SessionChangeSet();
SessionChangeSet(const SessionChangeSet &) = delete;
@@ -54,7 +53,7 @@ public:
}
void operator=(SessionChangeSet &);
- Utils::span<const byte> asSpan() const;
+ BlobView asBlobView() const;
friend void swap(SessionChangeSet &first, SessionChangeSet &second) noexcept
{
diff --git a/src/libs/sqlite/sqlitesessions.cpp b/src/libs/sqlite/sqlitesessions.cpp
index e377ef9b72..69c0e1fe4b 100644
--- a/src/libs/sqlite/sqlitesessions.cpp
+++ b/src/libs/sqlite/sqlitesessions.cpp
@@ -103,7 +103,7 @@ void Sessions::commit()
if (session && !sqlite3session_isempty(session.get())) {
SessionChangeSet changeSet{*this};
- insertSession.write(changeSet.asSpan());
+ insertSession.write(changeSet.asBlobView());
}
session.reset();
diff --git a/src/libs/sqlite/sqlitetimestamp.h b/src/libs/sqlite/sqlitetimestamp.h
new file mode 100644
index 0000000000..f30d71fb29
--- /dev/null
+++ b/src/libs/sqlite/sqlitetimestamp.h
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+namespace Sqlite {
+
+class TimeStamp
+{
+public:
+ TimeStamp() = default;
+ TimeStamp(long long value)
+ : value(value)
+ {}
+
+ friend bool operator==(TimeStamp first, TimeStamp second)
+ {
+ return first.value == second.value;
+ }
+
+public:
+ long long value = -1;
+};
+
+} // namespace Sqlite
diff --git a/src/libs/sqlite/sqlitevalue.h b/src/libs/sqlite/sqlitevalue.h
index 934db37642..8ace8a7bf1 100644
--- a/src/libs/sqlite/sqlitevalue.h
+++ b/src/libs/sqlite/sqlitevalue.h
@@ -23,6 +23,8 @@
**
****************************************************************************/
+#pragma once
+
#include "sqliteexception.h"
#include <utils/smallstring.h>
@@ -32,9 +34,6 @@
#include <cstddef>
-
-#pragma once
-
namespace Sqlite {
enum class ValueType : unsigned char { Null, Integer, Float, String };
diff --git a/src/plugins/projectexplorer/target.h b/src/plugins/projectexplorer/target.h
index 8de1d9168d..ce8440fa38 100644
--- a/src/plugins/projectexplorer/target.h
+++ b/src/plugins/projectexplorer/target.h
@@ -53,9 +53,11 @@ class PROJECTEXPLORER_EXPORT Target : public QObject
friend class SessionManager; // for setActiveBuild and setActiveDeployConfiguration
Q_OBJECT
- struct _constructor_tag { explicit _constructor_tag() = default; };
-
public:
+ struct _constructor_tag
+ {
+ explicit _constructor_tag() = default;
+ };
Target(Project *parent, Kit *k, _constructor_tag);
~Target() override;
diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt
index 291948a7e7..ed9340dcfb 100644
--- a/src/plugins/qmldesigner/CMakeLists.txt
+++ b/src/plugins/qmldesigner/CMakeLists.txt
@@ -6,7 +6,7 @@ endif()
add_qtc_plugin(QmlDesigner
DEPENDS
QmlJS LanguageUtils QmlEditorWidgets AdvancedDockingSystem
- Qt5::QuickWidgets Qt5::CorePrivate
+ Qt5::QuickWidgets Qt5::CorePrivate Sqlite
DEFINES
DESIGNER_CORE_LIBRARY
IDE_LIBRARY_BASENAME=\"${IDE_LIBRARY_BASE_PATH}\"
@@ -317,6 +317,7 @@ extend_qtc_plugin(QmlDesigner
itemlibraryassetimportdialog.cpp itemlibraryassetimportdialog.h
itemlibraryassetimportdialog.ui
itemlibraryassetimporter.cpp itemlibraryassetimporter.h
+ itemlibraryiconimageprovider.cpp itemlibraryiconimageprovider.h
)
find_package(Qt5 COMPONENTS Quick3DAssetImport QUIET)
@@ -502,6 +503,7 @@ extend_qtc_plugin(QmlDesigner
include/textmodifier.h
include/variantproperty.h
include/viewmanager.h
+ include/imagecache.h
)
extend_qtc_plugin(QmlDesigner
@@ -586,6 +588,21 @@ extend_qtc_plugin(QmlDesigner
pluginmanager/widgetpluginmanager.cpp pluginmanager/widgetpluginmanager.h
pluginmanager/widgetpluginpath.cpp pluginmanager/widgetpluginpath.h
rewritertransaction.cpp rewritertransaction.h
+
+ imagecache/imagecachecollector.h
+ imagecache/imagecachecollector.cpp
+ imagecache/imagecache.cpp
+ imagecache/imagecachecollectorinterface.h
+ imagecache/imagecacheconnectionmanager.cpp
+ imagecache/imagecacheconnectionmanager.h
+ imagecache/imagecachegenerator.cpp
+ imagecache/imagecachegenerator.h
+ imagecache/imagecachestorage.h
+ imagecache/imagecachegeneratorinterface.h
+ imagecache/imagecachestorageinterface.h
+ imagecache/timestampproviderinterface.h
+ imagecache/timestampprovider.h
+ imagecache/timestampprovider.cpp
)
extend_qtc_plugin(QmlDesigner
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.pri b/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.pri
index 0e6219dd9a..8eaa9ce983 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.pri
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.pri
@@ -7,6 +7,7 @@ qtHaveModule(quick3dassetimport) {
# Input
HEADERS += itemlibraryview.h \
+ $$PWD/itemlibraryiconimageprovider.h \
itemlibrarywidget.h \
itemlibrarymodel.h \
itemlibraryresourceview.h \
@@ -19,6 +20,7 @@ HEADERS += itemlibraryview.h \
customfilesystemmodel.h
SOURCES += itemlibraryview.cpp \
+ $$PWD/itemlibraryiconimageprovider.cpp \
itemlibrarywidget.cpp \
itemlibrarymodel.cpp \
itemlibraryresourceview.cpp \
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
index 1fc817cd01..ca00b66f46 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
@@ -502,7 +502,6 @@ bool ItemLibraryAssetImporter::generateComponentIcon(int size, const QString &ic
QProcessUniquePointer process = puppetCreator.createPuppetProcess(
"custom",
{},
- this,
std::function<void()>(),
[&](int exitCode, QProcess::ExitStatus exitStatus) {
processFinished(exitCode, exitStatus);
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp
new file mode 100644
index 0000000000..e0254111a9
--- /dev/null
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#include "itemlibraryiconimageprovider.h"
+
+#include <projectexplorer/target.h>
+#include <utils/stylehelper.h>
+
+#include <QMetaObject>
+#include <QQuickImageResponse>
+
+namespace QmlDesigner {
+
+class ImageRespose : public QQuickImageResponse
+{
+public:
+ QQuickTextureFactory *textureFactory() const override
+ {
+ return QQuickTextureFactory::textureFactoryForImage(m_image);
+ }
+
+ void setImage(const QImage &image)
+ {
+ m_image = image;
+
+ emit finished();
+ }
+
+ void abort()
+ {
+ m_image = QImage{
+ Utils::StyleHelper::dpiSpecificImageFile(":/ItemLibrary/images/item-default-icon.png")};
+
+ emit finished();
+ }
+
+private:
+ QImage m_image;
+};
+
+
+QQuickImageResponse *ItemLibraryIconImageProvider::requestImageResponse(const QString &id,
+ const QSize &)
+{
+ auto response = std::make_unique<ImageRespose>();
+
+ m_cache.requestIcon(
+ id,
+ [response = QPointer<ImageRespose>(response.get())](const QImage &image) {
+ QMetaObject::invokeMethod(
+ response,
+ [response, image] {
+ if (response)
+ response->setImage(image);
+ },
+ Qt::QueuedConnection);
+ },
+ [response = QPointer<ImageRespose>(response.get())] {
+ QMetaObject::invokeMethod(
+ response,
+ [response] {
+ if (response)
+ response->abort();
+ },
+ Qt::QueuedConnection);
+ });
+
+ return response.release();
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.h
new file mode 100644
index 0000000000..9e60246d4d
--- /dev/null
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <nodeinstanceview.h>
+#include <rewriterview.h>
+
+#include <coreplugin/icore.h>
+#include <imagecache.h>
+#include <imagecache/imagecachecollector.h>
+#include <imagecache/imagecacheconnectionmanager.h>
+#include <imagecache/imagecachegenerator.h>
+#include <imagecache/imagecachestorage.h>
+#include <imagecache/timestampprovider.h>
+
+#include <sqlitedatabase.h>
+
+#include <QQuickAsyncImageProvider>
+
+namespace QmlDesigner {
+
+class ItemLibraryIconImageProvider : public QQuickAsyncImageProvider
+{
+public:
+ ItemLibraryIconImageProvider(ImageCache &imageCache)
+ : m_cache{imageCache}
+ {}
+
+ QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override;
+
+private:
+ ImageCache &m_cache;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.cpp
index d67631531c..0d46140f12 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.cpp
@@ -47,8 +47,18 @@ QString ItemLibraryItem::typeName() const
QString ItemLibraryItem::itemLibraryIconPath() const
{
- //Prepend image provider prefix
- return QStringLiteral("image://qmldesigner_itemlibrary/") + m_itemLibraryEntry.libraryEntryIconPath();
+ if (m_itemLibraryEntry.customComponentSource().isEmpty()) {
+ return QStringLiteral("image://qmldesigner_itemlibrary/")
+ + m_itemLibraryEntry.libraryEntryIconPath();
+ } else {
+ return QStringLiteral("image://itemlibrary_preview/")
+ + m_itemLibraryEntry.customComponentSource();
+ }
+}
+
+QString ItemLibraryItem::componentPath() const
+{
+ return m_itemLibraryEntry.customComponentSource();
}
bool ItemLibraryItem::setVisible(bool isVisible)
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.h
index fa1fa92257..859cbe51dd 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.h
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.h
@@ -42,6 +42,7 @@ class ItemLibraryItem: public QObject {
Q_PROPERTY(QString itemName READ itemName FINAL)
Q_PROPERTY(QString itemLibraryIconPath READ itemLibraryIconPath FINAL)
Q_PROPERTY(bool itemVisible READ isVisible NOTIFY visibilityChanged FINAL)
+ Q_PROPERTY(QString componentPath READ componentPath FINAL)
public:
ItemLibraryItem(QObject *parent);
@@ -50,6 +51,7 @@ public:
QString itemName() const;
QString typeName() const;
QString itemLibraryIconPath() const;
+ QString componentPath() const;
bool setVisible(bool isVisible);
bool isVisible() const;
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
index a01ce38fc7..19506042bb 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
@@ -29,6 +29,8 @@
#include "itemlibraryitem.h"
#include "itemlibrarysection.h"
+#include <components/previewtooltip/previewtooltipbackend.h>
+
#include <model.h>
#include <nodehints.h>
#include <nodemetainfo.h>
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
index 5ffece61cd..79d559c0a0 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
@@ -25,24 +25,47 @@
#include "itemlibraryview.h"
#include "itemlibrarywidget.h"
+#include "metainfo.h"
+#include <bindingproperty.h>
+#include <coreplugin/icore.h>
+#include <imagecache.h>
+#include <imagecache/imagecachecollector.h>
+#include <imagecache/imagecacheconnectionmanager.h>
+#include <imagecache/imagecachegenerator.h>
+#include <imagecache/imagecachestorage.h>
+#include <imagecache/timestampprovider.h>
#include <import.h>
#include <importmanagerview.h>
-#include <qmlitemnode.h>
-#include <rewriterview.h>
-#include <bindingproperty.h>
#include <nodelistproperty.h>
+#include <projectexplorer/kit.h>
+#include <projectexplorer/target.h>
+#include <rewriterview.h>
+#include <sqlitedatabase.h>
#include <utils/algorithm.h>
#include <qmldesignerplugin.h>
-#include "metainfo.h"
+#include <qmlitemnode.h>
namespace QmlDesigner {
+class ImageCacheData
+{
+public:
+ Sqlite::Database database{
+ Utils::PathString{Core::ICore::cacheResourcePath() + "/imagecache-v1.db"}};
+ ImageCacheStorage<Sqlite::Database> storage{database};
+ ImageCacheConnectionManager connectionManager;
+ ImageCacheCollector collector{connectionManager};
+ ImageCacheGenerator generator{collector, storage};
+ TimeStampProvider timeStampProvider;
+ ImageCache cache{storage, generator, timeStampProvider};
+};
+
ItemLibraryView::ItemLibraryView(QObject* parent)
: AbstractView(parent),
m_importManagerView(new ImportManagerView(this))
{
-
+ m_imageCacheData = std::make_unique<ImageCacheData>();
}
ItemLibraryView::~ItemLibraryView() = default;
@@ -55,7 +78,7 @@ bool ItemLibraryView::hasWidget() const
WidgetInfo ItemLibraryView::widgetInfo()
{
if (m_widget.isNull()) {
- m_widget = new ItemLibraryWidget;
+ m_widget = new ItemLibraryWidget{m_imageCacheData->cache};
m_widget->setImportsWidget(m_importManagerView->widgetInfo().widget);
}
@@ -70,6 +93,16 @@ WidgetInfo ItemLibraryView::widgetInfo()
void ItemLibraryView::modelAttached(Model *model)
{
AbstractView::modelAttached(model);
+ auto target = QmlDesignerPlugin::instance()->currentDesignDocument()->currentTarget();
+ m_imageCacheData->cache.clean();
+
+ if (target) {
+ auto clonedTarget = std::make_unique<ProjectExplorer::Target>(
+ target->project(), target->kit()->clone(), ProjectExplorer::Target::_constructor_tag{});
+
+ m_imageCacheData->collector.setTarget(std::move(clonedTarget));
+ }
+
m_widget->clearSearchFilter();
m_widget->setModel(model);
updateImports();
@@ -83,6 +116,8 @@ void ItemLibraryView::modelAboutToBeDetached(Model *model)
{
model->detachView(m_importManagerView);
+ m_imageCacheData->collector.setTarget({});
+
AbstractView::modelAboutToBeDetached(model);
m_widget->setModel(nullptr);
@@ -124,7 +159,7 @@ void ItemLibraryView::importsChanged(const QList<Import> &addedImports, const QL
void ItemLibraryView::setResourcePath(const QString &resourcePath)
{
if (m_widget.isNull())
- m_widget = new ItemLibraryWidget;
+ m_widget = new ItemLibraryWidget{m_imageCacheData->cache};
m_widget->setResourcePath(resourcePath);
}
@@ -142,4 +177,4 @@ void ItemLibraryView::updateImports()
m_widget->delayedUpdateModel();
}
-} //QmlDesigner
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h
index 09535c3230..3d4af5d21c 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h
@@ -34,6 +34,7 @@ namespace QmlDesigner {
class ItemLibraryWidget;
class ImportManagerView;
+class ImageCacheData;
class ItemLibraryView : public AbstractView
{
@@ -58,6 +59,7 @@ protected:
void updateImports();
private:
+ std::unique_ptr<ImageCacheData> m_imageCacheData;
QPointer<ItemLibraryWidget> m_widget;
ImportManagerView *m_importManagerView;
bool m_hasErrors = false;
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp
index 45e5601135..10a253f5b6 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp
@@ -27,19 +27,21 @@
#include "customfilesystemmodel.h"
#include "itemlibraryassetimportdialog.h"
+#include "itemlibraryiconimageprovider.h"
#include <theme.h>
-#include <itemlibrarymodel.h>
+#include <designeractionmanager.h>
+#include <designermcumanager.h>
#include <itemlibraryimageprovider.h>
#include <itemlibraryinfo.h>
+#include <itemlibrarymodel.h>
#include <metainfo.h>
#include <model.h>
+#include <previewtooltip/previewtooltipbackend.h>
#include <rewritingexception.h>
-#include <qmldesignerplugin.h>
#include <qmldesignerconstants.h>
-#include <designeractionmanager.h>
-#include <designermcumanager.h>
+#include <qmldesignerplugin.h>
#include <utils/algorithm.h>
#include <utils/flowlayout.h>
@@ -81,14 +83,14 @@ static QString propertyEditorResourcesPath() {
return Core::ICore::resourcePath() + QStringLiteral("/qmldesigner/propertyEditorQmlSources");
}
-ItemLibraryWidget::ItemLibraryWidget(QWidget *parent) :
- QFrame(parent),
- m_itemIconSize(24, 24),
- m_itemViewQuickWidget(new QQuickWidget(this)),
- m_resourcesView(new ItemLibraryResourceView(this)),
- m_importTagsWidget(new QWidget(this)),
- m_addResourcesWidget(new QWidget(this)),
- m_filterFlag(QtBasic)
+ItemLibraryWidget::ItemLibraryWidget(ImageCache &imageCache)
+ : m_itemIconSize(24, 24)
+ , m_itemViewQuickWidget(new QQuickWidget(this))
+ , m_resourcesView(new ItemLibraryResourceView(this))
+ , m_importTagsWidget(new QWidget(this))
+ , m_addResourcesWidget(new QWidget(this))
+ , m_imageCache{imageCache}
+ , m_filterFlag(QtBasic)
{
m_compressionTimer.setInterval(200);
m_compressionTimer.setSingleShot(true);
@@ -102,16 +104,20 @@ ItemLibraryWidget::ItemLibraryWidget(QWidget *parent) :
m_itemViewQuickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
m_itemLibraryModel = new ItemLibraryModel(this);
- m_itemViewQuickWidget->rootContext()->setContextProperties(
- QVector<QQmlContext::PropertyPair>{
- {{"itemLibraryModel"}, QVariant::fromValue(m_itemLibraryModel.data())},
- {{"itemLibraryIconWidth"}, m_itemIconSize.width()},
- {{"itemLibraryIconHeight"}, m_itemIconSize.height()},
- {{"rootView"}, QVariant::fromValue(this)},
- {{"highlightColor"}, Utils::StyleHelper::notTooBrightHighlightColor()}
- }
- );
- m_itemViewQuickWidget->setClearColor(Theme::getColor(Theme::Color::QmlDesigner_BackgroundColorDarkAlternate));
+ m_itemViewQuickWidget->rootContext()->setContextProperties(QVector<QQmlContext::PropertyPair>{
+ {{"itemLibraryModel"}, QVariant::fromValue(m_itemLibraryModel.data())},
+ {{"itemLibraryIconWidth"}, m_itemIconSize.width()},
+ {{"itemLibraryIconHeight"}, m_itemIconSize.height()},
+ {{"rootView"}, QVariant::fromValue(this)},
+ {{"highlightColor"}, Utils::StyleHelper::notTooBrightHighlightColor()},
+ });
+
+ m_previewTooltipBackend = std::make_unique<PreviewTooltipBackend>(m_imageCache);
+ m_itemViewQuickWidget->rootContext()->setContextProperty("tooltipBackend",
+ m_previewTooltipBackend.get());
+
+ m_itemViewQuickWidget->setClearColor(
+ Theme::getColor(Theme::Color::QmlDesigner_BackgroundColorDarkAlternate));
/* create Resources view and its model */
m_resourcesFileSystemModel = new CustomFileSystemModel(this);
@@ -119,6 +125,7 @@ ItemLibraryWidget::ItemLibraryWidget(QWidget *parent) :
/* create image provider for loading item icons */
m_itemViewQuickWidget->engine()->addImageProvider(QStringLiteral("qmldesigner_itemlibrary"), new Internal::ItemLibraryImageProvider);
+
Theme::setupTheme(m_itemViewQuickWidget->engine());
/* other widgets */
@@ -243,6 +250,8 @@ ItemLibraryWidget::ItemLibraryWidget(QWidget *parent) :
reloadQmlSource();
}
+ItemLibraryWidget::~ItemLibraryWidget() = default;
+
void ItemLibraryWidget::setItemLibraryInfo(ItemLibraryInfo *itemLibraryInfo)
{
if (m_itemLibraryInfo.data() == itemLibraryInfo)
@@ -306,9 +315,14 @@ void ItemLibraryWidget::delayedUpdateModel()
void ItemLibraryWidget::setModel(Model *model)
{
+ m_itemViewQuickWidget->engine()->removeImageProvider("itemlibrary_preview");
m_model = model;
if (!model)
return;
+
+ m_itemViewQuickWidget->engine()->addImageProvider("itemlibrary_preview",
+ new ItemLibraryIconImageProvider{m_imageCache});
+
setItemLibraryInfo(model->metaInfo().itemLibraryInfo());
}
@@ -318,7 +332,8 @@ void ItemLibraryWidget::setCurrentIndexOfStackedWidget(int index)
m_filterLineEdit->setVisible(false);
m_importTagsWidget->setVisible(true);
m_addResourcesWidget->setVisible(false);
- } if (index == 1) {
+ }
+ if (index == 1) {
m_filterLineEdit->setVisible(true);
m_importTagsWidget->setVisible(false);
m_addResourcesWidget->setVisible(true);
@@ -564,5 +579,4 @@ void ItemLibraryWidget::addResources()
}
}
}
-
-}
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h
index 11dea7d0c1..bfe9106a23 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h
@@ -37,6 +37,8 @@
#include <QQmlPropertyMap>
#include <QTimer>
+#include <memory>
+
QT_BEGIN_NAMESPACE
class QStackedWidget;
class QShortcut;
@@ -52,6 +54,9 @@ class CustomFileSystemModel;
class ItemLibraryModel;
class ItemLibraryResourceView;
+class PreviewTooltipBackend;
+class ImageCache;
+class ImageCacheCollector;
class ItemLibraryWidget : public QFrame
{
@@ -63,7 +68,8 @@ class ItemLibraryWidget : public QFrame
};
public:
- ItemLibraryWidget(QWidget *parent = nullptr);
+ ItemLibraryWidget(ImageCache &imageCache);
+ ~ItemLibraryWidget();
void setItemLibraryInfo(ItemLibraryInfo *itemLibraryInfo);
QList<QToolButton *> createToolBarWidgets();
@@ -115,9 +121,10 @@ private:
QScopedPointer<ItemLibraryResourceView> m_resourcesView;
QScopedPointer<QWidget> m_importTagsWidget;
QScopedPointer<QWidget> m_addResourcesWidget;
+ std::unique_ptr<PreviewTooltipBackend> m_previewTooltipBackend;
QShortcut *m_qmlSourceUpdateShortcut;
-
+ ImageCache &m_imageCache;
QPointer<Model> m_model;
FilterChangeFlag m_filterFlag;
ItemLibraryEntry m_currentitemLibraryEntry;
diff --git a/src/plugins/qmldesigner/components/navigator/previewtooltip.ui b/src/plugins/qmldesigner/components/navigator/previewtooltip.ui
index ccfcb0a6c4..65e7cb01ce 100644
--- a/src/plugins/qmldesigner/components/navigator/previewtooltip.ui
+++ b/src/plugins/qmldesigner/components/navigator/previewtooltip.ui
@@ -82,12 +82,6 @@
</property>
<item>
<widget class="QLabel" name="imageLabel">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
<property name="minimumSize">
<size>
<width>150</width>
@@ -100,9 +94,6 @@
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
- <property name="text">
- <string notr="true">&lt;image&gt;</string>
- </property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp
new file mode 100644
index 0000000000..d3c972c217
--- /dev/null
+++ b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#include "previewimagetooltip.h"
+#include "ui_previewimagetooltip.h"
+
+#include <utils/theme/theme.h>
+
+#include <QtGui/qpixmap.h>
+
+namespace QmlDesigner {
+
+PreviewImageTooltip::PreviewImageTooltip(QWidget *parent)
+ : QWidget(parent)
+ , m_ui(std::make_unique<Ui::PreviewImageTooltip>())
+{
+ // setAttribute(Qt::WA_TransparentForMouseEvents);
+ setWindowFlags(Qt::ToolTip);
+ m_ui->setupUi(this);
+ setStyleSheet(QString("QWidget { background-color: %1 }").arg(Utils::creatorTheme()->color(Utils::Theme::BackgroundColorNormal).name()));
+}
+
+PreviewImageTooltip::~PreviewImageTooltip() = default;
+
+void PreviewImageTooltip::setComponentPath(const QString &path)
+{
+ m_ui->componentPathLabel->setText(path);
+}
+
+void PreviewImageTooltip::setComponentName(const QString &name)
+{
+ m_ui->componentNameLabel->setText(name);
+}
+
+void PreviewImageTooltip::setImage(const QImage &image)
+{
+ resize(image.width() + 20 + m_ui->componentNameLabel->width(),
+ std::max(image.height() + 20, height()));
+ m_ui->imageLabel->setPixmap(QPixmap::fromImage({image}));
+}
+}
diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.h b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.h
new file mode 100644
index 0000000000..e05b8a0727
--- /dev/null
+++ b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QtWidgets/qwidget.h>
+#include <QtGui/qpixmap.h>
+
+#include <memory>
+
+namespace QmlDesigner {
+namespace Ui {
+class PreviewImageTooltip;
+}
+
+class PreviewImageTooltip : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit PreviewImageTooltip(QWidget *parent = {});
+ ~PreviewImageTooltip();
+
+ void setComponentPath(const QString &path);
+ void setComponentName(const QString &name);
+ void setImage(const QImage &pixmap);
+
+private:
+ std::unique_ptr<Ui::PreviewImageTooltip> m_ui;
+};
+}
diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.ui b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.ui
new file mode 100644
index 0000000000..16f34fae07
--- /dev/null
+++ b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.ui
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QmlDesigner::PreviewImageTooltip</class>
+ <widget class="QWidget" name="QmlDesigner::PreviewImageTooltip">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>200</width>
+ <height>200</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>1000</width>
+ <height>1000</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string/>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="sizeGripEnabled" stdset="0">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>1</number>
+ </property>
+ <property name="topMargin">
+ <number>1</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QFrame" name="frame">
+ <property name="frameShape">
+ <enum>QFrame::Box</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="1">
+ <widget class="QLabel" name="componentPathLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>1</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string notr="true"/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::NoTextInteraction</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" rowspan="2">
+ <widget class="QLabel" name="imageLabel">
+ <property name="frameShape">
+ <enum>QFrame::Box</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="Utils::ElidingLabel" name="componentNameLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>1</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>12</pointsize>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string notr="true"/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::NoTextInteraction</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>Utils::ElidingLabel</class>
+ <extends>QLabel</extends>
+ <header location="global">utils/elidinglabel.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp
new file mode 100644
index 0000000000..559cd5c17b
--- /dev/null
+++ b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#include "previewtooltipbackend.h"
+
+#include "previewimagetooltip.h"
+
+#include <coreplugin/icore.h>
+#include <imagecache.h>
+
+#include <QApplication>
+#include <QDesktopWidget>
+#include <QMetaObject>
+
+namespace QmlDesigner {
+
+PreviewTooltipBackend::PreviewTooltipBackend(ImageCache &cache)
+ : m_cache{cache}
+{}
+
+PreviewTooltipBackend::~PreviewTooltipBackend()
+{
+ hideTooltip();
+}
+
+void PreviewTooltipBackend::showTooltip()
+{
+ if (m_componentPath.isEmpty())
+ return;
+
+ m_tooltip = std::make_unique<PreviewImageTooltip>();
+
+ m_tooltip->setComponentName(m_componentName);
+ m_tooltip->setComponentPath(m_componentPath);
+
+ m_cache.requestImage(
+ m_componentPath,
+ [tooltip = QPointer<PreviewImageTooltip>(m_tooltip.get())](const QImage &image) {
+ QMetaObject::invokeMethod(tooltip, [tooltip, image] {
+ if (tooltip)
+ tooltip->setImage(image);
+ });
+ },
+ [] {});
+
+ auto desktopWidget = QApplication::desktop();
+ auto mousePosition = desktopWidget->cursor().pos();
+
+ mousePosition += {20, 20};
+ m_tooltip->move(mousePosition);
+ m_tooltip->show();
+}
+
+void PreviewTooltipBackend::hideTooltip()
+{
+ if (m_tooltip)
+ m_tooltip->hide();
+
+ m_tooltip.reset();
+}
+
+QString QmlDesigner::PreviewTooltipBackend::componentPath() const
+{
+ return m_componentPath;
+}
+
+void QmlDesigner::PreviewTooltipBackend::setComponentPath(const QString &path)
+{
+ m_componentPath = path;
+
+ if (m_componentPath != path)
+ emit componentPathChanged();
+}
+
+QString QmlDesigner::PreviewTooltipBackend::componentName() const
+{
+ return m_componentName;
+}
+
+void QmlDesigner::PreviewTooltipBackend::setComponentName(const QString &name)
+{
+ m_componentName = name;
+
+ if (m_componentName != name)
+ emit componentNameChanged();
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.h b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.h
new file mode 100644
index 0000000000..b5f777662b
--- /dev/null
+++ b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QObject>
+#include <QQmlEngine>
+
+#include <memory>
+
+namespace QmlDesigner {
+
+class PreviewImageTooltip;
+class ImageCache;
+
+class PreviewTooltipBackend : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QString componentPath READ componentPath WRITE setComponentPath NOTIFY componentPathChanged)
+ Q_PROPERTY(QString componentName READ componentName WRITE setComponentName NOTIFY componentNameChanged)
+
+public:
+ PreviewTooltipBackend(ImageCache &cache);
+ ~PreviewTooltipBackend();
+
+ Q_INVOKABLE void showTooltip();
+ Q_INVOKABLE void hideTooltip();
+
+ QString componentPath() const;
+ void setComponentPath(const QString &path);
+
+ QString componentName() const;
+ void setComponentName(const QString &path);
+
+signals:
+ void componentPathChanged();
+ void componentNameChanged();
+
+private:
+ QString m_componentPath;
+ QString m_componentName;
+ std::unique_ptr<PreviewImageTooltip> m_tooltip;
+ ImageCache &m_cache;
+};
+
+}
+
+QML_DECLARE_TYPE(QmlDesigner::PreviewTooltipBackend)
diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.pri b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.pri
new file mode 100644
index 0000000000..a33c5d9853
--- /dev/null
+++ b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.pri
@@ -0,0 +1,11 @@
+HEADERS += \
+ $$PWD/previewtooltipbackend.h \
+ $$PWD/previewimagetooltip.h
+
+SOURCES += \
+ $$PWD/previewtooltipbackend.cpp \
+ $$PWD/previewimagetooltip.cpp
+
+FORMS += $$PWD/previewimagetooltip.ui
+
+
diff --git a/src/plugins/qmldesigner/designercore/designercore-lib.pri b/src/plugins/qmldesigner/designercore/designercore-lib.pri
index 08842527ee..981025d1fb 100644
--- a/src/plugins/qmldesigner/designercore/designercore-lib.pri
+++ b/src/plugins/qmldesigner/designercore/designercore-lib.pri
@@ -14,6 +14,7 @@ include (../../../../share/qtcreator/qml/qmlpuppet/container/container.pri)
include (../../../../share/qtcreator/qml/qmlpuppet/types/types.pri)
SOURCES += $$PWD/model/abstractview.cpp \
+ $$PWD/imagecache/imagecachecollector.cpp \
$$PWD/model/rewriterview.cpp \
$$PWD/model/documentmessage.cpp \
$$PWD/metainfo/metainfo.cpp \
@@ -84,9 +85,15 @@ SOURCES += $$PWD/model/abstractview.cpp \
$$PWD/model/qmltimeline.cpp \
$$PWD/model/qmltimelinekeyframegroup.cpp \
$$PWD/model/annotation.cpp \
- $$PWD/model/stylesheetmerger.cpp
+ $$PWD/model/stylesheetmerger.cpp \
+ $$PWD/imagecache/imagecache.cpp \
+ $$PWD/imagecache/imagecacheconnectionmanager.cpp \
+ $$PWD/imagecache/imagecachegenerator.cpp \
+ $$PWD/imagecache/timestampprovider.cpp
+
HEADERS += $$PWD/include/qmldesignercorelib_global.h \
+ $$PWD/imagecache/imagecachecollector.h \
$$PWD/include/abstractview.h \
$$PWD/include/nodeinstanceview.h \
$$PWD/include/rewriterview.h \
@@ -162,7 +169,17 @@ HEADERS += $$PWD/include/qmldesignercorelib_global.h \
$$PWD/include/qmltimeline.h \
$$PWD/include/qmltimelinekeyframegroup.h \
$$PWD/include/annotation.h \
- $$PWD/include/stylesheetmerger.h
+ $$PWD/include/stylesheetmerger.h \
+ $$PWD/include/imagecache.h \
+ $$PWD/imagecache/imagecachecollectorinterface.h \
+ $$PWD/imagecache/imagecacheconnectionmanager.h \
+ $$PWD/imagecache/imagecachegeneratorinterface.h \
+ $$PWD/imagecache/imagecachestorageinterface.h \
+ $$PWD/imagecache/imagecachegenerator.h \
+ $$PWD/imagecache/imagecachestorage.h \
+ $$PWD/imagecache/timestampprovider.h \
+ $$PWD/imagecache/timestampproviderinterface.h
+
FORMS += \
$$PWD/instances/puppetdialog.ui
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecache.cpp
new file mode 100644
index 0000000000..20409eb7fa
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecache.cpp
@@ -0,0 +1,166 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#include "imagecache.h"
+
+#include "imagecachegenerator.h"
+#include "imagecachestorage.h"
+#include "timestampprovider.h"
+
+#include <thread>
+
+namespace QmlDesigner {
+
+ImageCache::ImageCache(ImageCacheStorageInterface &storage,
+ ImageCacheGeneratorInterface &generator,
+ TimeStampProviderInterface &timeStampProvider)
+ : m_storage(storage)
+ , m_generator(generator)
+ , m_timeStampProvider(timeStampProvider)
+{
+ m_backgroundThread = std::thread{[this] {
+ while (isRunning()) {
+ if (auto [hasEntry, entry] = getEntry(); hasEntry) {
+ request(entry.name,
+ entry.requestType,
+ std::move(entry.captureCallback),
+ std::move(entry.abortCallback),
+ m_storage,
+ m_generator,
+ m_timeStampProvider);
+ }
+
+ waitForEntries();
+ }
+ }};
+}
+
+ImageCache::~ImageCache()
+{
+ clean();
+ stopThread();
+ m_condition.notify_all();
+ if (m_backgroundThread.joinable())
+ m_backgroundThread.join();
+}
+
+void ImageCache::request(Utils::SmallStringView name,
+ ImageCache::RequestType requestType,
+ ImageCache::CaptureCallback captureCallback,
+ ImageCache::AbortCallback abortCallback,
+ ImageCacheStorageInterface &storage,
+ ImageCacheGeneratorInterface &generator,
+ TimeStampProviderInterface &timeStampProvider)
+{
+ const auto timeStamp = timeStampProvider.timeStamp(name);
+ const auto entry = requestType == RequestType::Image ? storage.fetchImage(name, timeStamp)
+ : storage.fetchIcon(name, timeStamp);
+
+ if (entry.hasEntry) {
+ if (entry.image.isNull())
+ abortCallback();
+ else
+ captureCallback(entry.image);
+ } else {
+ generator.generateImage(name, timeStamp, std::move(captureCallback), std::move(abortCallback));
+ }
+}
+
+void ImageCache::requestImage(Utils::PathString name,
+ ImageCache::CaptureCallback captureCallback,
+ AbortCallback abortCallback)
+{
+ addEntry(std::move(name), std::move(captureCallback), std::move(abortCallback), RequestType::Image);
+ m_condition.notify_all();
+}
+
+void ImageCache::requestIcon(Utils::PathString name,
+ ImageCache::CaptureCallback captureCallback,
+ ImageCache::AbortCallback abortCallback)
+{
+ addEntry(std::move(name), std::move(captureCallback), std::move(abortCallback), RequestType::Icon);
+ m_condition.notify_all();
+}
+
+void ImageCache::clean()
+{
+ clearEntries();
+ m_generator.clean();
+}
+
+std::tuple<bool, ImageCache::Entry> ImageCache::getEntry()
+{
+ std::unique_lock lock{m_mutex};
+
+ if (m_entries.empty())
+ return {false, Entry{}};
+
+ Entry entry = m_entries.back();
+ m_entries.pop_back();
+
+ return {true, entry};
+}
+
+void ImageCache::addEntry(Utils::PathString &&name,
+ ImageCache::CaptureCallback &&captureCallback,
+ AbortCallback &&abortCallback,
+ RequestType requestType)
+{
+ std::unique_lock lock{m_mutex};
+
+ m_entries.emplace_back(std::move(name),
+ std::move(captureCallback),
+ std::move(abortCallback),
+ requestType);
+}
+
+void ImageCache::clearEntries()
+{
+ std::unique_lock lock{m_mutex};
+ for (Entry &entry : m_entries)
+ entry.abortCallback();
+ m_entries.clear();
+}
+
+void ImageCache::waitForEntries()
+{
+ std::unique_lock lock{m_mutex};
+ if (m_entries.empty())
+ m_condition.wait(lock, [&] { return m_entries.size() || m_finishing; });
+}
+
+void ImageCache::stopThread()
+{
+ std::unique_lock lock{m_mutex};
+ m_finishing = true;
+}
+
+bool ImageCache::isRunning()
+{
+ std::unique_lock lock{m_mutex};
+ return !m_finishing;
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp
new file mode 100644
index 0000000000..1bb7262d17
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#include "imagecachecollector.h"
+#include "imagecacheconnectionmanager.h"
+
+#include <metainfo.h>
+#include <model.h>
+#include <nodeinstanceview.h>
+#include <plaintexteditmodifier.h>
+#include <rewriterview.h>
+
+#include <projectexplorer/project.h>
+#include <projectexplorer/target.h>
+#include <utils/fileutils.h>
+
+#include <QPlainTextEdit>
+
+namespace QmlDesigner {
+
+namespace {
+
+QByteArray fileToByteArray(QString const &filename)
+{
+ QFile file(filename);
+ QFileInfo fleInfo(file);
+
+ if (fleInfo.exists() && file.open(QFile::ReadOnly))
+ return file.readAll();
+
+ return {};
+}
+
+QString fileToString(const QString &filename)
+{
+ return QString::fromUtf8(fileToByteArray(filename));
+}
+
+} // namespace
+
+ImageCacheCollector::ImageCacheCollector(ImageCacheConnectionManager &connectionManager)
+ : m_connectionManager{connectionManager}
+{}
+
+ImageCacheCollector::~ImageCacheCollector() = default;
+
+void ImageCacheCollector::start(Utils::SmallStringView name,
+ CaptureCallback captureCallback,
+ AbortCallback abortCallback)
+{
+ RewriterView rewriterView{RewriterView::Amend, nullptr};
+ NodeInstanceView nodeInstanceView{m_connectionManager};
+
+ const QString filePath{name};
+ std::unique_ptr<Model> model{QmlDesigner::Model::create("QtQuick/Item", 2, 1)};
+ model->setFileUrl(QUrl::fromLocalFile(filePath));
+
+ auto textDocument = std::make_unique<QTextDocument>(fileToString(filePath));
+
+ auto modifier = std::make_unique<NotIndentingTextEditModifier>(textDocument.get(),
+ QTextCursor{textDocument.get()});
+
+ rewriterView.setTextModifier(modifier.get());
+
+ model->setRewriterView(&rewriterView);
+
+ if (rewriterView.inErrorState() || !rewriterView.rootModelNode().metaInfo().isGraphicalItem()) {
+ abortCallback();
+ return;
+ }
+
+ m_connectionManager.setCallback(std::move(captureCallback));
+
+ nodeInstanceView.setTarget(m_target.get());
+ nodeInstanceView.setCrashCallback(abortCallback);
+ model->setNodeInstanceView(&nodeInstanceView);
+
+ bool capturedDataArrived = m_connectionManager.waitForCapturedData();
+
+ m_connectionManager.setCallback({});
+ m_connectionManager.setCrashCallback({});
+
+ model->setNodeInstanceView({});
+ model->setRewriterView({});
+
+ if (!capturedDataArrived)
+ abortCallback();
+}
+
+void ImageCacheCollector::setTarget(std::unique_ptr<ProjectExplorer::Target> target)
+{
+ m_target = std::move(target);
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h
new file mode 100644
index 0000000000..e39f95f573
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "imagecachecollectorinterface.h"
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+class QTextDocument;
+QT_END_NAMESPACE
+
+namespace ProjectExplorer {
+class Target;
+}
+
+namespace QmlDesigner {
+
+class Model;
+class NotIndentingTextEditModifier;
+class ImageCacheConnectionManager;
+class RewriterView;
+class NodeInstanceView;
+
+class ImageCacheCollector final : public ImageCacheCollectorInterface
+{
+public:
+ ImageCacheCollector(ImageCacheConnectionManager &connectionManager);
+
+ ~ImageCacheCollector();
+
+ void start(Utils::SmallStringView filePath,
+ CaptureCallback captureCallback,
+ AbortCallback abortCallback) override;
+
+ void setTarget(std::unique_ptr<ProjectExplorer::Target> target);
+
+private:
+ ImageCacheConnectionManager &m_connectionManager;
+ std::unique_ptr<ProjectExplorer::Target> m_target;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h
new file mode 100644
index 0000000000..e6528f2ec3
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <utils/smallstringview.h>
+
+#include <QImage>
+
+namespace QmlDesigner {
+
+class ImageCacheCollectorInterface
+{
+public:
+ using CaptureCallback = std::function<void(QImage &&image)>;
+ using AbortCallback = std::function<void()>;
+
+ virtual void start(Utils::SmallStringView filePath,
+ CaptureCallback captureCallback,
+ AbortCallback abortCallback)
+ = 0;
+
+protected:
+ ~ImageCacheCollectorInterface() = default;
+};
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecacheconnectionmanager.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecacheconnectionmanager.cpp
new file mode 100644
index 0000000000..2c7982bd1f
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecacheconnectionmanager.cpp
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#include "imagecacheconnectionmanager.h"
+
+#include <captureddatacommand.h>
+
+#include <QLocalSocket>
+
+namespace QmlDesigner {
+
+ImageCacheConnectionManager::ImageCacheConnectionManager()
+{
+ connections().emplace_back("Capture icon", "captureiconmode");
+}
+
+void ImageCacheConnectionManager::setCallback(ImageCacheConnectionManager::Callback callback)
+{
+ m_captureCallback = std::move(callback);
+}
+
+bool ImageCacheConnectionManager::waitForCapturedData()
+{
+ if (connections().empty())
+ return false;
+
+ disconnect(connections().front().socket.get(), &QIODevice::readyRead, nullptr, nullptr);
+
+ while (!m_capturedDataArrived) {
+ bool dataArrived = connections().front().socket->waitForReadyRead(600000);
+
+ if (!dataArrived)
+ return false;
+
+ readDataStream(connections().front());
+ }
+
+ m_capturedDataArrived = false;
+
+ return true;
+}
+
+void ImageCacheConnectionManager::dispatchCommand(const QVariant &command,
+ ConnectionManagerInterface::Connection &)
+{
+ static const int capturedDataCommandType = QMetaType::type("CapturedDataCommand");
+
+ if (command.userType() == capturedDataCommandType) {
+ m_captureCallback(command.value<CapturedDataCommand>().image);
+ m_capturedDataArrived = true;
+ }
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecacheconnectionmanager.h b/src/plugins/qmldesigner/designercore/imagecache/imagecacheconnectionmanager.h
new file mode 100644
index 0000000000..5788f6f31d
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecacheconnectionmanager.h
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <designercore/instances/connectionmanager.h>
+
+namespace QmlDesigner {
+
+class CapturedDataCommand;
+
+class ImageCacheConnectionManager : public ConnectionManager
+{
+public:
+ using Callback = std::function<void(QImage &&)>;
+
+ ImageCacheConnectionManager();
+
+ void setCallback(Callback captureCallback);
+
+ bool waitForCapturedData();
+
+protected:
+ void dispatchCommand(const QVariant &command, Connection &connection) override;
+
+private:
+ Callback m_captureCallback;
+ bool m_capturedDataArrived = false;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp
new file mode 100644
index 0000000000..a6783fbf48
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#include "imagecachegenerator.h"
+
+#include "imagecachecollectorinterface.h"
+#include "imagecachestorage.h"
+
+#include <QThread>
+
+namespace QmlDesigner {
+
+ImageCacheGenerator::~ImageCacheGenerator()
+{
+ std::lock_guard threadLock{*m_threadMutex.get()};
+
+ if (m_backgroundThread)
+ m_backgroundThread->wait();
+
+ clean();
+}
+
+void ImageCacheGenerator::generateImage(Utils::SmallStringView name,
+ Sqlite::TimeStamp timeStamp,
+ ImageCacheGeneratorInterface::CaptureCallback &&captureCallback,
+ AbortCallback &&abortCallback)
+{
+ {
+ std::lock_guard lock{m_dataMutex};
+ m_tasks.emplace_back(name, timeStamp, std::move(captureCallback), std::move(abortCallback));
+ }
+
+ startGenerationAsynchronously();
+}
+
+void ImageCacheGenerator::clean()
+{
+ std::lock_guard dataLock{m_dataMutex};
+ m_tasks.clear();
+}
+
+class ReleaseProcessing
+{
+public:
+ ReleaseProcessing(std::atomic_flag &processing)
+ : m_processing(processing)
+ {
+ m_processing.test_and_set(std::memory_order_acquire);
+ }
+
+ ~ReleaseProcessing() { m_processing.clear(std::memory_order_release); }
+
+private:
+ std::atomic_flag &m_processing;
+};
+
+void ImageCacheGenerator::startGeneration(std::shared_ptr<std::mutex> threadMutex)
+{
+ ReleaseProcessing guard(m_processing);
+
+ while (true) {
+ Task task;
+
+ {
+ std::unique_lock threadLock{*threadMutex.get(), std::defer_lock_t{}};
+
+ if (!threadLock.try_lock())
+ return;
+
+ std::lock_guard dataLock{m_dataMutex};
+
+ if (m_tasks.empty()) {
+ m_storage.walCheckpointFull();
+ return;
+ }
+
+ task = std::move(m_tasks.back());
+
+ m_tasks.pop_back();
+ }
+
+ m_collector.start(
+ task.filePath,
+ [this, threadMutex, task](QImage &&image) {
+ std::unique_lock lock{*threadMutex.get(), std::defer_lock_t{}};
+
+ if (!lock.try_lock())
+ return;
+
+ if (threadMutex.use_count() == 1)
+ return;
+
+ if (image.isNull())
+ task.abortCallback();
+ else
+ task.captureCallback(image);
+
+ m_storage.storeImage(std::move(task.filePath), task.timeStamp, image);
+ },
+ [this, threadMutex, task] {
+ std::unique_lock lock{*threadMutex.get(), std::defer_lock_t{}};
+
+ if (!lock.try_lock())
+ return;
+
+ if (threadMutex.use_count() == 1)
+ return;
+
+ task.abortCallback();
+ m_storage.storeImage(std::move(task.filePath), task.timeStamp, {});
+ });
+ }
+}
+
+void ImageCacheGenerator::startGenerationAsynchronously()
+{
+ if (m_processing.test_and_set(std::memory_order_acquire))
+ return;
+
+ std::unique_lock lock{*m_threadMutex.get(), std::defer_lock_t{}};
+
+ if (!lock.try_lock())
+ return;
+
+ if (m_backgroundThread)
+ m_backgroundThread->wait();
+
+ m_backgroundThread.reset(QThread::create(
+ [this](std::shared_ptr<std::mutex> threadMutex) { startGeneration(threadMutex); },
+ m_threadMutex));
+ m_backgroundThread->start();
+ // m_backgroundThread = std::thread(
+ // [this](std::shared_ptr<std::mutex> threadMutex) { startGeneration(threadMutex); },
+ // m_threadMutex);
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h
new file mode 100644
index 0000000000..207622714b
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "imagecachegeneratorinterface.h"
+
+#include <utils/smallstring.h>
+
+#include <QThread>
+
+#include <memory>
+#include <mutex>
+
+QT_BEGIN_NAMESPACE
+class QPlainTextEdit;
+QT_END_NAMESPACE
+
+namespace QmlDesigner {
+
+class ImageCacheCollectorInterface;
+class ImageCacheStorageInterface;
+
+class ImageCacheGenerator final : public ImageCacheGeneratorInterface
+{
+public:
+ ImageCacheGenerator(ImageCacheCollectorInterface &collector, ImageCacheStorageInterface &storage)
+ : m_collector{collector}
+ , m_storage(storage)
+ {}
+
+ ~ImageCacheGenerator();
+
+ void generateImage(Utils::SmallStringView filePath,
+ Sqlite::TimeStamp timeStamp,
+ CaptureCallback &&captureCallback,
+ AbortCallback &&abortCallback) override;
+ void clean() override;
+
+private:
+ struct Task
+ {
+ Task() = default;
+ Task(Utils::SmallStringView filePath,
+ Sqlite::TimeStamp timeStamp,
+ CaptureCallback &&captureCallback,
+ AbortCallback &&abortCallback)
+ : filePath(filePath)
+ , captureCallback(std::move(captureCallback))
+ , abortCallback(std::move(abortCallback))
+ , timeStamp(timeStamp)
+ {}
+
+ Utils::PathString filePath;
+ CaptureCallback captureCallback;
+ AbortCallback abortCallback;
+ Sqlite::TimeStamp timeStamp;
+ };
+
+ void startGeneration(std::shared_ptr<std::mutex> threadMutex);
+ void startGenerationAsynchronously();
+
+private:
+ std::unique_ptr<QThread> m_backgroundThread;
+ std::mutex m_dataMutex;
+ std::shared_ptr<std::mutex> m_threadMutex{std::make_shared<std::mutex>()};
+ std::vector<Task> m_tasks;
+ ImageCacheCollectorInterface &m_collector;
+ ImageCacheStorageInterface &m_storage;
+ std::atomic_flag m_processing = ATOMIC_FLAG_INIT;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h
new file mode 100644
index 0000000000..26b9621995
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <sqlitetimestamp.h>
+#include <utils/smallstringview.h>
+
+#include <QImage>
+
+namespace QmlDesigner {
+
+class ImageCacheGeneratorInterface
+{
+public:
+ using CaptureCallback = std::function<void(const QImage &image)>;
+ using AbortCallback = std::function<void()>;
+
+ virtual void generateImage(Utils::SmallStringView name,
+ Sqlite::TimeStamp timeStamp,
+ CaptureCallback &&captureCallback,
+ AbortCallback &&abortCallback)
+ = 0;
+
+ virtual void clean() = 0;
+
+protected:
+ ~ImageCacheGeneratorInterface() = default;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h
new file mode 100644
index 0000000000..90900cf19e
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h
@@ -0,0 +1,197 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#include "imagecachestorageinterface.h"
+
+#include <createtablesqlstatementbuilder.h>
+
+#include <sqliteblob.h>
+#include <sqlitereadstatement.h>
+#include <sqlitetable.h>
+#include <sqlitetransaction.h>
+#include <sqlitewritestatement.h>
+
+#include <QBuffer>
+#include <QImageReader>
+#include <QImageWriter>
+
+namespace QmlDesigner {
+
+template<typename DatabaseType>
+class ImageCacheStorage : public ImageCacheStorageInterface
+{
+public:
+ using ReadStatement = typename DatabaseType::ReadStatement;
+ using WriteStatement = typename DatabaseType::WriteStatement;
+
+ ImageCacheStorage(DatabaseType &database)
+ : database(database)
+ {
+ transaction.commit();
+ }
+
+ Entry fetchImage(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const override
+ {
+ try {
+ Sqlite::DeferredTransaction transaction{database};
+
+ auto optionalBlob = selectImageStatement.template value<Sqlite::ByteArrayBlob>(
+ name, minimumTimeStamp.value);
+
+ transaction.commit();
+
+ if (optionalBlob) {
+ QBuffer buffer{&optionalBlob->byteArray};
+ QImageReader reader{&buffer, "PNG"};
+
+ return Entry{reader.read(), true};
+ }
+
+ return {};
+
+ } catch (const Sqlite::StatementIsBusy &) {
+ return fetchImage(name, minimumTimeStamp);
+ }
+ }
+
+ Entry fetchIcon(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const override
+ {
+ try {
+ Sqlite::DeferredTransaction transaction{database};
+
+ auto optionalBlob = selectIconStatement.template value<Sqlite::ByteArrayBlob>(
+ name, minimumTimeStamp.value);
+
+ transaction.commit();
+
+ if (optionalBlob) {
+ QBuffer buffer{&optionalBlob->byteArray};
+ QImageReader reader{&buffer, "PNG"};
+
+ return Entry{reader.read(), true};
+ }
+
+ return {};
+
+ } catch (const Sqlite::StatementIsBusy &) {
+ return fetchIcon(name, minimumTimeStamp);
+ }
+ }
+
+ void storeImage(Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QImage &image) override
+ {
+ try {
+ Sqlite::ImmediateTransaction transaction{database};
+
+ if (image.isNull()) {
+ upsertImageStatement.write(name,
+ newTimeStamp.value,
+ Sqlite::NullValue{},
+ Sqlite::NullValue{});
+ } else {
+ QSize iconSize = image.size().scaled(QSize{96, 96}.boundedTo(image.size()),
+ Qt::KeepAspectRatio);
+ QImage icon = image.scaled(iconSize);
+ upsertImageStatement.write(name,
+ newTimeStamp.value,
+ Sqlite::BlobView{createImageBuffer(image)->data()},
+ Sqlite::BlobView{createImageBuffer(icon)->data()});
+ }
+ transaction.commit();
+
+ } catch (const Sqlite::StatementIsBusy &) {
+ return storeImage(name, newTimeStamp, image);
+ }
+ }
+
+ void walCheckpointFull()
+ {
+ try {
+ database.walCheckpointFull();
+ } catch (const Sqlite::StatementIsBusy &) {
+ return walCheckpointFull();
+ }
+ }
+
+private:
+ class Initializer
+ {
+ public:
+ Initializer(DatabaseType &database)
+ {
+ if (!database.isInitialized()) {
+ Sqlite::ExclusiveTransaction transaction{database};
+
+ createImagesTable(database);
+
+ transaction.commit();
+
+ database.setIsInitialized(true);
+
+ database.walCheckpointFull();
+ }
+ }
+
+ void createImagesTable(DatabaseType &database)
+ {
+ Sqlite::Table table;
+ table.setUseIfNotExists(true);
+ table.setName("images");
+ table.addColumn("id", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}});
+ table.addColumn("name", Sqlite::ColumnType::Text, {Sqlite::NotNull{}, Sqlite::Unique{}});
+ table.addColumn("mtime", Sqlite::ColumnType::Integer);
+ table.addColumn("image", Sqlite::ColumnType::Blob);
+ table.addColumn("icon", Sqlite::ColumnType::Blob);
+
+ table.initialize(database);
+ }
+ };
+
+ std::unique_ptr<QBuffer> createImageBuffer(const QImage &image)
+ {
+ auto buffer = std::make_unique<QBuffer>();
+ buffer->open(QIODevice::WriteOnly);
+ QImageWriter writer{buffer.get(), "PNG"};
+ writer.write(image);
+
+ return buffer;
+ }
+
+public:
+ DatabaseType &database;
+ Initializer initializer{database};
+ Sqlite::ImmediateNonThrowingDestructorTransaction transaction{database};
+ mutable ReadStatement selectImageStatement{
+ "SELECT image FROM images WHERE name=?1 AND mtime >= ?2", database};
+ mutable ReadStatement selectIconStatement{
+ "SELECT icon FROM images WHERE name=?1 AND mtime >= ?2", database};
+ WriteStatement upsertImageStatement{
+ "INSERT INTO images(name, mtime, image, icon) VALUES (?1, ?2, ?3, ?4) ON "
+ "CONFLICT(name) DO UPDATE SET mtime=excluded.mtime, image=excluded.image, "
+ "icon=excluded.icon",
+ database};
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachestorageinterface.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorageinterface.h
new file mode 100644
index 0000000000..97ace6efe6
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorageinterface.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QImage>
+
+#include <sqlitetimestamp.h>
+#include <utils/smallstringview.h>
+
+namespace QmlDesigner {
+namespace Internal {
+class ImageCacheStorageEntry
+{
+ public:
+ QImage image;
+ bool hasEntry = false;
+};
+
+} // namespace Internal
+
+class ImageCacheStorageInterface
+{
+public:
+ using Entry = Internal::ImageCacheStorageEntry;
+
+ virtual Entry fetchImage(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const = 0;
+ virtual Entry fetchIcon(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const = 0;
+ virtual void storeImage(Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QImage &image) = 0;
+ virtual void walCheckpointFull() = 0;
+
+protected:
+ ~ImageCacheStorageInterface() = default;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/timestampprovider.cpp b/src/plugins/qmldesigner/designercore/imagecache/timestampprovider.cpp
new file mode 100644
index 0000000000..99573f175f
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/timestampprovider.cpp
@@ -0,0 +1,38 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#include "timestampprovider.h"
+
+#include <QDateTime>
+#include <QFileInfo>
+
+namespace QmlDesigner {
+
+Sqlite::TimeStamp TimeStampProvider::timeStamp(Utils::SmallStringView name) const
+{
+ return QFileInfo{QString{name}}.lastModified().toSecsSinceEpoch();
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/timestampprovider.h b/src/plugins/qmldesigner/designercore/imagecache/timestampprovider.h
new file mode 100644
index 0000000000..8acc5fcb58
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/timestampprovider.h
@@ -0,0 +1,39 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "timestampproviderinterface.h"
+
+namespace QmlDesigner {
+
+class TimeStampProvider : public TimeStampProviderInterface
+{
+public:
+ Sqlite::TimeStamp timeStamp(Utils::SmallStringView name) const override;
+};
+
+} // namespace QmlDesigner
+
diff --git a/src/plugins/qmldesigner/designercore/imagecache/timestampproviderinterface.h b/src/plugins/qmldesigner/designercore/imagecache/timestampproviderinterface.h
new file mode 100644
index 0000000000..33cffc9b49
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/timestampproviderinterface.h
@@ -0,0 +1,44 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <sqlitetimestamp.h>
+#include <utils/smallstringview.h>
+
+#include <QImage>
+
+namespace QmlDesigner {
+
+class TimeStampProviderInterface
+{
+public:
+ virtual Sqlite::TimeStamp timeStamp(Utils::SmallStringView name) const = 0;
+
+protected:
+ ~TimeStampProviderInterface() = default;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/include/basetexteditmodifier.h b/src/plugins/qmldesigner/designercore/include/basetexteditmodifier.h
index e20566724b..640ff367bc 100644
--- a/src/plugins/qmldesigner/designercore/include/basetexteditmodifier.h
+++ b/src/plugins/qmldesigner/designercore/include/basetexteditmodifier.h
@@ -49,6 +49,9 @@ public:
bool renameId(const QString &oldId, const QString &newId) override;
bool moveToComponent(int nodeOffset) override;
QStringList autoComplete(QTextDocument *textDocument, int position, bool explicitComplete) override;
+
+private:
+ TextEditor::TextEditorWidget *m_textEdit;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/include/filesystemfacadeinterface.h b/src/plugins/qmldesigner/designercore/include/filesystemfacadeinterface.h
new file mode 100644
index 0000000000..c0ba644fdd
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/include/filesystemfacadeinterface.h
@@ -0,0 +1,37 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+namespace QmlDesigner {
+
+class FileSystemFacadeInterface
+{
+public:
+protected:
+ ~FileSystemFacadeInterface() = default;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/include/imagecache.h b/src/plugins/qmldesigner/designercore/include/imagecache.h
new file mode 100644
index 0000000000..4ac360c2a5
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/include/imagecache.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <utils/smallstring.h>
+
+#include <condition_variable>
+#include <functional>
+#include <mutex>
+#include <thread>
+
+#include <QImage>
+
+namespace QmlDesigner {
+
+class TimeStampProviderInterface;
+class ImageCacheStorageInterface;
+class ImageCacheGeneratorInterface;
+
+class ImageCache
+{
+public:
+ using CaptureCallback = std::function<void(const QImage &)>;
+ using AbortCallback = std::function<void()>;
+
+ ~ImageCache();
+
+ ImageCache(ImageCacheStorageInterface &storage,
+ ImageCacheGeneratorInterface &generator,
+ TimeStampProviderInterface &timeStampProvider);
+
+ void requestImage(Utils::PathString name,
+ CaptureCallback captureCallback,
+ AbortCallback abortCallback);
+ void requestIcon(Utils::PathString name,
+ CaptureCallback captureCallback,
+ AbortCallback abortCallback);
+
+ void clean();
+
+private:
+ enum class RequestType { Image, Icon };
+ struct Entry
+ {
+ Entry() = default;
+ Entry(Utils::PathString name,
+ CaptureCallback &&captureCallback,
+ AbortCallback &&abortCallback,
+ RequestType requestType)
+ : name{std::move(name)}
+ , captureCallback{std::move(captureCallback)}
+ , abortCallback{std::move(abortCallback)}
+ , requestType{requestType}
+ {}
+
+ Utils::PathString name;
+ CaptureCallback captureCallback;
+ AbortCallback abortCallback;
+ RequestType requestType = RequestType::Image;
+ };
+
+ std::tuple<bool, Entry> getEntry();
+ void addEntry(Utils::PathString &&name,
+ CaptureCallback &&captureCallback,
+ AbortCallback &&abortCallback,
+ RequestType requestType);
+ void clearEntries();
+ void waitForEntries();
+ void stopThread();
+ bool isRunning();
+ static void request(Utils::SmallStringView name,
+ ImageCache::RequestType requestType,
+ ImageCache::CaptureCallback captureCallback,
+ ImageCache::AbortCallback abortCallback,
+ ImageCacheStorageInterface &storage,
+ ImageCacheGeneratorInterface &generator,
+ TimeStampProviderInterface &timeStampProvider);
+
+private:
+ std::vector<Entry> m_entries;
+ mutable std::mutex m_mutex;
+ std::condition_variable m_condition;
+ std::thread m_backgroundThread;
+ ImageCacheStorageInterface &m_storage;
+ ImageCacheGeneratorInterface &m_generator;
+ TimeStampProviderInterface &m_timeStampProvider;
+ bool m_finishing{false};
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h
index a570a0e690..0e86ad11ee 100644
--- a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h
+++ b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h
@@ -146,6 +146,11 @@ public:
QVariant previewImageDataForGenericNode(const ModelNode &modelNode, const ModelNode &renderNode);
QVariant previewImageDataForImageNode(const ModelNode &modelNode);
+ void setCrashCallback(std::function<void()> crashCallback)
+ {
+ m_crashCallback = std::move(crashCallback);
+ }
+
protected:
void timerEvent(QTimerEvent *event) override;
@@ -231,6 +236,7 @@ private:
// key: fileUrl value: (key: instance qml id, value: related tool states)
QHash<QUrl, QHash<QString, QVariantMap>> m_edit3DToolStates;
+ std::function<void()> m_crashCallback{[this] { handleCrash(); }};
};
} // namespace ProxyNodeInstanceView
diff --git a/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h b/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h
index 500bffd6fd..c18b4d8cfb 100644
--- a/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h
+++ b/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h
@@ -47,6 +47,7 @@ private:
public:
PlainTextEditModifier(QPlainTextEdit *textEdit);
+ PlainTextEditModifier(QTextDocument *document, const QTextCursor &textCursor);
~PlainTextEditModifier() override;
QTextDocument *textDocument() const override;
@@ -76,20 +77,17 @@ public:
bool moveToComponent(int /* nodeOffset */) override
{ return false; }
-protected:
- QPlainTextEdit *plainTextEdit() const
- { return m_textEdit; }
-
private:
void textEditChanged();
void runRewriting(Utils::ChangeSet *writer);
private:
- Utils::ChangeSet *m_changeSet;
- QPlainTextEdit *m_textEdit;
- bool m_changeSignalsEnabled;
- bool m_pendingChangeSignal;
- bool m_ongoingTextChange;
+ Utils::ChangeSet *m_changeSet = nullptr;
+ QTextDocument *m_textDocument;
+ QTextCursor m_textCursor;
+ bool m_changeSignalsEnabled{true};
+ bool m_pendingChangeSignal{false};
+ bool m_ongoingTextChange{false};
};
class QMLDESIGNERCORE_EXPORT NotIndentingTextEditModifier: public PlainTextEditModifier
@@ -99,6 +97,10 @@ public:
: PlainTextEditModifier(textEdit)
{}
+ NotIndentingTextEditModifier(QTextDocument *document, const QTextCursor &textCursor)
+ : PlainTextEditModifier{document, textCursor}
+ {}
+
void indent(int /*offset*/, int /*length*/) override
{}
void indentLines(int /*offset*/, int /*length*/) override
diff --git a/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp b/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp
index b7a2cc282e..76d641fe30 100644
--- a/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp
@@ -33,11 +33,12 @@
namespace QmlDesigner {
-void BaseConnectionManager::setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
+void BaseConnectionManager::setUp(NodeInstanceServerInterface *nodeInstanceServer,
const QString &,
- ProjectExplorer::Target *)
+ ProjectExplorer::Target *,
+ AbstractView *view)
{
- m_nodeInstanceServerProxy = nodeInstanceServerProxy;
+ m_nodeInstanceServer = nodeInstanceServer;
m_isActive = true;
}
@@ -47,7 +48,14 @@ void BaseConnectionManager::shutDown()
writeCommand(QVariant::fromValue(EndPuppetCommand()));
- m_nodeInstanceServerProxy = nullptr;
+ m_nodeInstanceServer = nullptr;
+}
+
+void BaseConnectionManager::setCrashCallback(std::function<void()> callback)
+{
+ std::lock_guard<std::mutex> lock{m_callbackMutex};
+
+ m_crashCallback = std::move(callback);
}
bool BaseConnectionManager::isActive() const
@@ -85,7 +93,7 @@ void BaseConnectionManager::dispatchCommand(const QVariant &command, Connection
if (!isActive())
return;
- m_nodeInstanceServerProxy->dispatchCommand(command);
+ m_nodeInstanceServer->dispatchCommand(command);
}
void BaseConnectionManager::readDataStream(Connection &connection)
@@ -123,5 +131,12 @@ void BaseConnectionManager::readDataStream(Connection &connection)
for (const QVariant &command : commandList)
dispatchCommand(command, connection);
}
+
+void BaseConnectionManager::callCrashCallback()
+{
+ std::lock_guard<std::mutex> lock{m_callbackMutex};
+
+ m_crashCallback();
+}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.h b/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.h
index 83a41a2bd8..fca035682f 100644
--- a/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.h
+++ b/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.h
@@ -29,6 +29,8 @@
#include <QProcess>
+#include <mutex>
+
QT_BEGIN_NAMESPACE
class QLocalSocket;
QT_END_NAMESPACE
@@ -40,7 +42,6 @@ class Target;
namespace QmlDesigner {
class AbstractView;
-class NodeInstanceServerProxy;
class QMLDESIGNERCORE_EXPORT BaseConnectionManager : public QObject, public ConnectionManagerInterface
{
@@ -49,11 +50,14 @@ class QMLDESIGNERCORE_EXPORT BaseConnectionManager : public QObject, public Conn
public:
BaseConnectionManager() = default;
- void setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
+ void setUp(NodeInstanceServerInterface *nodeInstanceServer,
const QString &qrcMappingString,
- ProjectExplorer::Target *target) override;
+ ProjectExplorer::Target *target,
+ AbstractView *view) override;
void shutDown() override;
+ void setCrashCallback(std::function<void()> callback) override;
+
bool isActive() const;
protected:
@@ -61,15 +65,19 @@ protected:
virtual void showCannotConnectToPuppetWarningAndSwitchToEditMode();
using ConnectionManagerInterface::processFinished;
void processFinished();
- void writeCommandToIODevice(const QVariant &command,
- QIODevice *ioDevice,
- unsigned int commandCounter);
+ static void writeCommandToIODevice(const QVariant &command,
+ QIODevice *ioDevice,
+ unsigned int commandCounter);
void readDataStream(Connection &connection);
- NodeInstanceServerProxy *nodeInstanceServerProxy() const { return m_nodeInstanceServerProxy; }
+ NodeInstanceServerInterface *nodeInstanceServer() const { return m_nodeInstanceServer; }
+
+ void callCrashCallback();
private:
- NodeInstanceServerProxy *m_nodeInstanceServerProxy{};
+ std::mutex m_callbackMutex;
+ std::function<void()> m_crashCallback;
+ NodeInstanceServerInterface *m_nodeInstanceServer{};
bool m_isActive = false;
};
diff --git a/src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.cpp b/src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.cpp
index adf395d874..90f4226217 100644
--- a/src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.cpp
@@ -34,11 +34,12 @@
namespace QmlDesigner {
-void CapturingConnectionManager::setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
+void CapturingConnectionManager::setUp(NodeInstanceServerInterface *nodeInstanceServer,
const QString &qrcMappingString,
- ProjectExplorer::Target *target)
+ ProjectExplorer::Target *target,
+ AbstractView *view)
{
- InteractiveConnectionManager::setUp(nodeInstanceServerProxy, qrcMappingString, target);
+ InteractiveConnectionManager::setUp(nodeInstanceServer, qrcMappingString, target, view);
int indexOfCapturePuppetStream = QCoreApplication::arguments().indexOf(
"-capture-puppet-stream");
@@ -72,7 +73,7 @@ void CapturingConnectionManager::writeCommand(const QVariant &command)
if (m_captureFileForTest.isWritable()) {
qDebug() << "command name: " << QMetaType::typeName(command.userType());
- writeCommandToIODevice(command, &m_captureFileForTest, m_writeCommandCounter);
+ writeCommandToIODevice(command, &m_captureFileForTest, writeCommandCounter());
qDebug() << "\tcatpure file offset: " << m_captureFileForTest.pos();
}
}
diff --git a/src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.h b/src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.h
index 1bedef440b..e13d2e254a 100644
--- a/src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.h
+++ b/src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.h
@@ -32,9 +32,10 @@ namespace QmlDesigner {
class QMLDESIGNERCORE_EXPORT CapturingConnectionManager : public InteractiveConnectionManager
{
public:
- void setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
+ void setUp(NodeInstanceServerInterface *nodeInstanceServer,
const QString &qrcMappingString,
- ProjectExplorer::Target *target) override;
+ ProjectExplorer::Target *target,
+ AbstractView *view) override;
void processFinished(int exitCode, QProcess::ExitStatus exitStatus) override;
diff --git a/src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp b/src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp
index fa8528579d..77ea8706bf 100644
--- a/src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp
@@ -46,19 +46,19 @@ ConnectionManager::ConnectionManager() = default;
ConnectionManager::~ConnectionManager() = default;
-void ConnectionManager::setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
+void ConnectionManager::setUp(NodeInstanceServerInterface *nodeInstanceServerProxy,
const QString &qrcMappingString,
- ProjectExplorer::Target *target)
+ ProjectExplorer::Target *target,
+ AbstractView *view)
{
- BaseConnectionManager::setUp(nodeInstanceServerProxy, qrcMappingString, target);
+ BaseConnectionManager::setUp(nodeInstanceServerProxy, qrcMappingString, target, view);
m_localServer = std::make_unique<QLocalServer>();
QString socketToken(QUuid::createUuid().toString());
m_localServer->listen(socketToken);
m_localServer->setMaxPendingConnections(3);
- NodeInstanceView *nodeInstanceView = nodeInstanceServerProxy->nodeInstanceView();
- PuppetCreator puppetCreator(target, nodeInstanceView->model());
+ PuppetCreator puppetCreator(target, view->model());
puppetCreator.setQrcMappingString(qrcMappingString);
puppetCreator.createQml2PuppetExecutableIfMissing();
@@ -67,7 +67,6 @@ void ConnectionManager::setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
connection.qmlPuppetProcess = puppetCreator.createPuppetProcess(
connection.mode,
socketToken,
- nodeInstanceView,
[&] { printProcessOutput(connection.qmlPuppetProcess.get(), connection.name); },
[&](int exitCode, QProcess::ExitStatus exitStatus) {
processFinished(exitCode, exitStatus);
@@ -90,7 +89,7 @@ void ConnectionManager::setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
if (connectedToPuppet) {
connection.socket.reset(m_localServer->nextPendingConnection());
- QObject::connect(connection.socket.get(), &QIODevice::readyRead, [&] {
+ QObject::connect(connection.socket.get(), &QIODevice::readyRead, this, [&] {
readDataStream(connection);
});
} else {
@@ -101,11 +100,6 @@ void ConnectionManager::setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
}
m_localServer->close();
-
- connect(this,
- &ConnectionManager::processCrashed,
- nodeInstanceServerProxy,
- &NodeInstanceServerProxy::processCrashed);
}
void ConnectionManager::shutDown()
@@ -143,7 +137,7 @@ void ConnectionManager::processFinished(int exitCode, QProcess::ExitStatus exitS
closeSocketsAndKillProcesses();
if (exitStatus == QProcess::CrashExit)
- emit processCrashed();
+ callCrashCallback();
}
void ConnectionManager::closeSocketsAndKillProcesses()
diff --git a/src/plugins/qmldesigner/designercore/instances/connectionmanager.h b/src/plugins/qmldesigner/designercore/instances/connectionmanager.h
index 3e8ef26744..c3c2c34afb 100644
--- a/src/plugins/qmldesigner/designercore/instances/connectionmanager.h
+++ b/src/plugins/qmldesigner/designercore/instances/connectionmanager.h
@@ -48,19 +48,20 @@ public:
~ConnectionManager() override;
enum PuppetStreamType { FirstPuppetStream, SecondPuppetStream, ThirdPuppetStream };
- void setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
+ void setUp(NodeInstanceServerInterface *nodeInstanceServerProxy,
const QString &qrcMappingString,
- ProjectExplorer::Target *target) override;
+ ProjectExplorer::Target *target,
+ AbstractView *view) override;
void shutDown() override;
void writeCommand(const QVariant &command) override;
-signals:
- void processCrashed();
-
protected:
using BaseConnectionManager::processFinished;
void processFinished(int exitCode, QProcess::ExitStatus exitStatus) override;
+ std::vector<Connection> &connections() { return m_connections; }
+
+ quint32 &writeCommandCounter() { return m_writeCommandCounter; }
private:
void printProcessOutput(QProcess *process, const QString &connectionName);
@@ -69,7 +70,6 @@ private:
private:
std::unique_ptr<QLocalServer> m_localServer;
-protected:
std::vector<Connection> m_connections;
quint32 m_writeCommandCounter = 0;
};
diff --git a/src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.h b/src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.h
index 92d7449bc0..2fc75c61c2 100644
--- a/src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.h
+++ b/src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.h
@@ -38,7 +38,8 @@ class Target;
namespace QmlDesigner {
-class NodeInstanceServerProxy;
+class NodeInstanceServerInterface;
+class AbstractView;
class QMLDESIGNERCORE_EXPORT ConnectionManagerInterface
{
@@ -65,12 +66,15 @@ public:
virtual ~ConnectionManagerInterface();
- virtual void setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
+ virtual void setUp(NodeInstanceServerInterface *nodeInstanceServer,
const QString &qrcMappingString,
- ProjectExplorer::Target *target)
+ ProjectExplorer::Target *target,
+ AbstractView *view)
= 0;
virtual void shutDown() = 0;
+ virtual void setCrashCallback(std::function<void()> callback) = 0;
+
virtual void writeCommand(const QVariant &command) = 0;
protected:
diff --git a/src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.cpp b/src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.cpp
index 6da44603df..cd9b5fc5cf 100644
--- a/src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.cpp
@@ -38,20 +38,21 @@ namespace QmlDesigner {
InteractiveConnectionManager::InteractiveConnectionManager()
{
- m_connections.emplace_back("Editor", "editormode");
- m_connections.emplace_back("Render", "rendermode");
- m_connections.emplace_back("Preview", "previewmode");
+ connections().emplace_back("Editor", "editormode");
+ connections().emplace_back("Render", "rendermode");
+ connections().emplace_back("Preview", "previewmode");
}
-void InteractiveConnectionManager::setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
+void InteractiveConnectionManager::setUp(NodeInstanceServerInterface *nodeInstanceServer,
const QString &qrcMappingString,
- ProjectExplorer::Target *target)
+ ProjectExplorer::Target *target,
+ AbstractView *view)
{
- ConnectionManager::setUp(nodeInstanceServerProxy, qrcMappingString, target);
+ ConnectionManager::setUp(nodeInstanceServer, qrcMappingString, target, view);
DesignerSettings settings = QmlDesignerPlugin::instance()->settings();
int timeOutTime = settings.value(DesignerSettingsKey::PUPPET_KILL_TIMEOUT).toInt();
- for (Connection &connection : m_connections)
+ for (Connection &connection : connections())
connection.timer->setInterval(timeOutTime);
if (QmlDesignerPlugin::instance()
@@ -59,7 +60,7 @@ void InteractiveConnectionManager::setUp(NodeInstanceServerProxy *nodeInstanceSe
.value(DesignerSettingsKey::DEBUG_PUPPET)
.toString()
.isEmpty()) {
- for (Connection &connection : m_connections) {
+ for (Connection &connection : connections()) {
QObject::connect(connection.timer.get(), &QTimer::timeout, [&]() {
puppetTimeout(connection);
});
@@ -67,6 +68,12 @@ void InteractiveConnectionManager::setUp(NodeInstanceServerProxy *nodeInstanceSe
}
}
+void InteractiveConnectionManager::shutDown()
+{
+ m_view = {};
+ ConnectionManager::shutDown();
+}
+
void InteractiveConnectionManager::showCannotConnectToPuppetWarningAndSwitchToEditMode()
{
Core::AsynchronousMessageBox::warning(
@@ -75,8 +82,8 @@ void InteractiveConnectionManager::showCannotConnectToPuppetWarningAndSwitchToEd
"Switching to another kit might help."));
QmlDesignerPlugin::instance()->switchToTextModeDeferred();
- nodeInstanceServerProxy()->nodeInstanceView()->emitDocumentMessage(
- tr("Cannot Connect to QML Emulation Layer (QML Puppet)"));
+ if (m_view)
+ m_view->emitDocumentMessage(tr("Cannot Connect to QML Emulation Layer (QML Puppet)"));
}
void InteractiveConnectionManager::dispatchCommand(const QVariant &command, Connection &connection)
diff --git a/src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.h b/src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.h
index 1946620a43..03be103ad6 100644
--- a/src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.h
+++ b/src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.h
@@ -34,9 +34,12 @@ class InteractiveConnectionManager : public ConnectionManager
public:
InteractiveConnectionManager();
- void setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
+ void setUp(NodeInstanceServerInterface *nodeInstanceServer,
const QString &qrcMappingString,
- ProjectExplorer::Target *target) override;
+ ProjectExplorer::Target *target,
+ AbstractView *view) override;
+
+ void shutDown() override;
void showCannotConnectToPuppetWarningAndSwitchToEditMode() override;
@@ -46,6 +49,9 @@ protected:
private:
void puppetTimeout(Connection &connection);
void puppetAlive(Connection &connection);
+
+private:
+ AbstractView *m_view{};
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp
index 173adc1b00..e04d725b38 100644
--- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp
@@ -100,7 +100,7 @@ NodeInstanceServerProxy::NodeInstanceServerProxy(NodeInstanceView *nodeInstanceV
if (instanceViewBenchmark().isInfoEnabled())
m_benchmarkTimer.start();
- m_connectionManager.setUp(this, qrcMappingString(), target);
+ m_connectionManager.setUp(this, qrcMappingString(), target, nodeInstanceView);
qCInfo(instanceViewBenchmark) << "puppets setup:" << m_benchmarkTimer.elapsed();
}
diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h
index 0177bd6a14..e4edeb6725 100644
--- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h
+++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h
@@ -84,6 +84,7 @@ public:
void requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command) override;
void changeLanguage(const ChangeLanguageCommand &command) override;
void changePreviewImageSize(const ChangePreviewImageSizeCommand &command) override;
+ void dispatchCommand(const QVariant &command) override;
NodeInstanceView *nodeInstanceView() const { return m_nodeInstanceView; }
@@ -91,12 +92,8 @@ public:
protected:
void writeCommand(const QVariant &command);
- void dispatchCommand(const QVariant &command);
NodeInstanceClientInterface *nodeInstanceClient() const;
-signals:
- void processCrashed();
-
private:
NodeInstanceView *m_nodeInstanceView{};
QElapsedTimer m_benchmarkTimer;
diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
index e2ac5f6356..3f0b1f8a5f 100644
--- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
@@ -196,10 +196,7 @@ void NodeInstanceView::modelAttached(Model *model)
AbstractView::modelAttached(model);
m_nodeInstanceServer = createNodeInstanceServerProxy();
m_lastCrashTime.start();
- connect(m_nodeInstanceServer.get(),
- &NodeInstanceServerProxy::processCrashed,
- this,
- &NodeInstanceView::handleCrash);
+ m_connectionManager.setCrashCallback(m_crashCallback);
if (!isSkippedRootNode(rootModelNode())) {
m_nodeInstanceServer->createScene(createCreateSceneCommand());
@@ -215,6 +212,8 @@ void NodeInstanceView::modelAttached(Model *model)
void NodeInstanceView::modelAboutToBeDetached(Model * model)
{
+ m_connectionManager.setCrashCallback({});
+
removeAllInstanceNodeRelationships();
if (m_nodeInstanceServer) {
m_nodeInstanceServer->clearScene(createClearSceneCommand());
@@ -281,11 +280,6 @@ void NodeInstanceView::restartProcess()
m_nodeInstanceServer.reset();
m_nodeInstanceServer = createNodeInstanceServerProxy();
- connect(m_nodeInstanceServer.get(),
- &NodeInstanceServerProxy::processCrashed,
- this,
- &NodeInstanceView::handleCrash);
-
if (!isSkippedRootNode(rootModelNode())) {
m_nodeInstanceServer->createScene(createCreateSceneCommand());
m_nodeInstanceServer->changeSelection(
diff --git a/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp b/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp
index 5f691bc5a0..df609f4cb0 100644
--- a/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp
@@ -181,7 +181,6 @@ PuppetCreator::PuppetCreator(ProjectExplorer::Target *target, const Model *model
QProcessUniquePointer PuppetCreator::createPuppetProcess(
const QString &puppetMode,
const QString &socketToken,
- QObject *handlerObject,
std::function<void()> processOutputCallback,
std::function<void(int, QProcess::ExitStatus)> processFinishCallback,
const QStringList &customOptions) const
@@ -190,7 +189,6 @@ QProcessUniquePointer PuppetCreator::createPuppetProcess(
qmlPuppetDirectory(m_availablePuppetType),
puppetMode,
socketToken,
- handlerObject,
processOutputCallback,
processFinishCallback,
customOptions);
@@ -201,7 +199,6 @@ QProcessUniquePointer PuppetCreator::puppetProcess(
const QString &workingDirectory,
const QString &puppetMode,
const QString &socketToken,
- QObject *handlerObject,
std::function<void()> processOutputCallback,
std::function<void(int, QProcess::ExitStatus)> processFinishCallback,
const QStringList &customOptions) const
@@ -216,7 +213,6 @@ QProcessUniquePointer PuppetCreator::puppetProcess(
&QProcess::kill);
QObject::connect(puppetProcess.get(),
static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
- handlerObject,
processFinishCallback);
#ifndef QMLDESIGNER_TEST
@@ -227,7 +223,7 @@ QProcessUniquePointer PuppetCreator::puppetProcess(
#endif
if (forwardOutput == puppetMode || forwardOutput == "all") {
puppetProcess->setProcessChannelMode(QProcess::MergedChannels);
- QObject::connect(puppetProcess.get(), &QProcess::readyRead, handlerObject, processOutputCallback);
+ QObject::connect(puppetProcess.get(), &QProcess::readyRead, processOutputCallback);
}
puppetProcess->setWorkingDirectory(workingDirectory);
diff --git a/src/plugins/qmldesigner/designercore/instances/puppetcreator.h b/src/plugins/qmldesigner/designercore/instances/puppetcreator.h
index 69c66688fe..e001b9d0c1 100644
--- a/src/plugins/qmldesigner/designercore/instances/puppetcreator.h
+++ b/src/plugins/qmldesigner/designercore/instances/puppetcreator.h
@@ -58,7 +58,6 @@ public:
QProcessUniquePointer createPuppetProcess(
const QString &puppetMode,
const QString &socketToken,
- QObject *handlerObject,
std::function<void()> processOutputCallback,
std::function<void(int, QProcess::ExitStatus)> processFinishCallback,
const QStringList &customOptions = {}) const;
@@ -89,7 +88,6 @@ protected:
const QString &workingDirectory,
const QString &puppetMode,
const QString &socketToken,
- QObject *handlerObject,
std::function<void()> processOutputCallback,
std::function<void(int, QProcess::ExitStatus)> processFinishCallback,
const QStringList &customOptions) const;
diff --git a/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp b/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp
index 03239faedc..8113e62846 100644
--- a/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp
+++ b/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp
@@ -38,8 +38,9 @@
using namespace QmlDesigner;
-BaseTextEditModifier::BaseTextEditModifier(TextEditor::TextEditorWidget *textEdit):
- PlainTextEditModifier(textEdit)
+BaseTextEditModifier::BaseTextEditModifier(TextEditor::TextEditorWidget *textEdit)
+ : PlainTextEditModifier(textEdit)
+ , m_textEdit{textEdit}
{
}
@@ -47,21 +48,20 @@ void BaseTextEditModifier::indentLines(int startLine, int endLine)
{
if (startLine < 0)
return;
- auto baseTextEditorWidget = qobject_cast<TextEditor::TextEditorWidget*>(plainTextEdit());
- if (!baseTextEditorWidget)
+
+ if (!m_textEdit)
return;
- QTextDocument *textDocument = plainTextEdit()->document();
- TextEditor::TextDocument *baseTextEditorDocument = baseTextEditorWidget->textDocument();
+ TextEditor::TextDocument *baseTextEditorDocument = m_textEdit->textDocument();
TextEditor::TabSettings tabSettings = baseTextEditorDocument->tabSettings();
- QTextCursor tc(textDocument);
+ QTextCursor tc(textDocument());
tc.beginEditBlock();
for (int i = startLine; i <= endLine; i++) {
- QTextBlock start = textDocument->findBlockByNumber(i);
+ QTextBlock start = textDocument()->findBlockByNumber(i);
if (start.isValid()) {
- QmlJSEditor::Internal::Indenter indenter(textDocument);
+ QmlJSEditor::Internal::Indenter indenter(textDocument());
indenter.indentBlock(start, QChar::Null, tabSettings);
}
}
@@ -82,22 +82,23 @@ void BaseTextEditModifier::indent(int offset, int length)
int BaseTextEditModifier::indentDepth() const
{
- if (auto bte = qobject_cast<TextEditor::TextEditorWidget*>(plainTextEdit()))
- return bte->textDocument()->tabSettings().m_indentSize;
+ if (m_textEdit)
+ return m_textEdit->textDocument()->tabSettings().m_indentSize;
else
return 0;
}
bool BaseTextEditModifier::renameId(const QString &oldId, const QString &newId)
{
- if (auto bte = qobject_cast<TextEditor::TextEditorWidget*>(plainTextEdit())) {
- if (auto document = qobject_cast<QmlJSEditor::QmlJSEditorDocument *>(bte->textDocument())) {
+ if (m_textEdit) {
+ if (auto document = qobject_cast<QmlJSEditor::QmlJSEditorDocument *>(
+ m_textEdit->textDocument())) {
Utils::ChangeSet changeSet;
foreach (const QmlJS::SourceLocation &loc,
document->semanticInfo().idLocations.value(oldId)) {
changeSet.replace(loc.begin(), loc.end(), newId);
}
- QTextCursor tc = bte->textCursor();
+ QTextCursor tc = textCursor();
changeSet.apply(&tc);
return true;
}
@@ -120,10 +121,9 @@ static QmlJS::AST::UiObjectDefinition *getObjectDefinition(const QList<QmlJS::AS
bool BaseTextEditModifier::moveToComponent(int nodeOffset)
{
- if (auto bte = qobject_cast<TextEditor::TextEditorWidget*>(plainTextEdit())) {
- if (auto document
- = qobject_cast<QmlJSEditor::QmlJSEditorDocument *>(bte->textDocument())) {
-
+ if (m_textEdit) {
+ if (auto document = qobject_cast<QmlJSEditor::QmlJSEditorDocument *>(
+ m_textEdit->textDocument())) {
auto qualifiedId = QmlJS::AST::cast<QmlJS::AST::UiQualifiedId *>(document->semanticInfo().astNodeAt(nodeOffset));
QList<QmlJS::AST::Node *> path = document->semanticInfo().rangePath(nodeOffset);
QmlJS::AST::UiObjectDefinition *object = getObjectDefinition(path, qualifiedId);
@@ -140,9 +140,9 @@ bool BaseTextEditModifier::moveToComponent(int nodeOffset)
QStringList BaseTextEditModifier::autoComplete(QTextDocument *textDocument, int position, bool explicitComplete)
{
- if (auto bte = qobject_cast<TextEditor::TextEditorWidget*>(plainTextEdit()))
- if (auto document
- = qobject_cast<QmlJSEditor::QmlJSEditorDocument *>(bte->textDocument()))
+ if (m_textEdit)
+ if (auto document = qobject_cast<QmlJSEditor::QmlJSEditorDocument *>(
+ m_textEdit->textDocument()))
return QmlJSEditor::qmlJSAutoComplete(textDocument,
position,
document->filePath(),
diff --git a/src/plugins/qmldesigner/designercore/model/plaintexteditmodifier.cpp b/src/plugins/qmldesigner/designercore/model/plaintexteditmodifier.cpp
index f87dc73a2c..782ff3794b 100644
--- a/src/plugins/qmldesigner/designercore/model/plaintexteditmodifier.cpp
+++ b/src/plugins/qmldesigner/designercore/model/plaintexteditmodifier.cpp
@@ -35,19 +35,17 @@
using namespace Utils;
using namespace QmlDesigner;
-PlainTextEditModifier::PlainTextEditModifier(QPlainTextEdit *textEdit):
- m_changeSet(nullptr),
- m_textEdit(textEdit),
- m_changeSignalsEnabled(true),
- m_pendingChangeSignal(false),
- m_ongoingTextChange(false)
+PlainTextEditModifier::PlainTextEditModifier(QPlainTextEdit *textEdit)
+ : PlainTextEditModifier(textEdit->document(), textEdit->textCursor())
{
- Q_ASSERT(textEdit);
-
- connect(m_textEdit, &QPlainTextEdit::textChanged,
- this, &PlainTextEditModifier::textEditChanged);
+ connect(textEdit, &QPlainTextEdit::textChanged, this, &PlainTextEditModifier::textEditChanged);
}
+PlainTextEditModifier::PlainTextEditModifier(QTextDocument *document, const QTextCursor &textCursor)
+ : m_textDocument{document}
+ , m_textCursor{textCursor}
+{}
+
PlainTextEditModifier::~PlainTextEditModifier() = default;
void PlainTextEditModifier::replace(int offset, int length, const QString &replacement)
@@ -158,17 +156,17 @@ void PlainTextEditModifier::runRewriting(ChangeSet *changeSet)
QTextDocument *PlainTextEditModifier::textDocument() const
{
- return m_textEdit->document();
+ return m_textDocument;
}
QString PlainTextEditModifier::text() const
{
- return m_textEdit->toPlainText();
+ return m_textDocument->toPlainText();
}
QTextCursor PlainTextEditModifier::textCursor() const
{
- return m_textEdit->textCursor();
+ return m_textCursor;
}
void PlainTextEditModifier::deactivateChangeSignals()
diff --git a/src/plugins/qmldesigner/qmldesigner_dependencies.pri b/src/plugins/qmldesigner/qmldesigner_dependencies.pri
index 321d2c2b07..473692aa5d 100644
--- a/src/plugins/qmldesigner/qmldesigner_dependencies.pri
+++ b/src/plugins/qmldesigner/qmldesigner_dependencies.pri
@@ -3,7 +3,8 @@ QTC_LIB_DEPENDS += \
utils \
qmljs \
qmleditorwidgets \
- advanceddockingsystem
+ advanceddockingsystem \
+ sqlite
QTC_PLUGIN_DEPENDS += \
coreplugin \
texteditor \
diff --git a/src/plugins/qmldesigner/qmldesignerplugin.pro b/src/plugins/qmldesigner/qmldesignerplugin.pro
index 6590dcb046..8f34f42d0a 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.pro
+++ b/src/plugins/qmldesigner/qmldesignerplugin.pro
@@ -35,6 +35,7 @@ include(components/annotationeditor/annotationeditor.pri)
include(components/richtexteditor/richtexteditor.pri)
include(components/transitioneditor/transitioneditor.pri)
include(components/listmodeleditor/listmodeleditor.pri)
+include(components/previewtooltip/previewtooltipbackend.pri)
BUILD_PUPPET_IN_CREATOR_BINPATH = $$(BUILD_PUPPET_IN_CREATOR_BINPATH)
!isEmpty(BUILD_PUPPET_IN_CREATOR_BINPATH) {
diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs
index a4e22d7520..3d73f1c05e 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.qbs
+++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs
@@ -27,6 +27,7 @@ Project {
Depends { name: "LanguageUtils" }
Depends { name: "QtSupport" }
Depends { name: "app_version_header" }
+ Depends { name: "Sqlite" }
cpp.defines: base.concat([
"DESIGNER_CORE_LIBRARY",
@@ -411,6 +412,21 @@ Project {
"pluginmanager/widgetpluginmanager.h",
"pluginmanager/widgetpluginpath.cpp",
"pluginmanager/widgetpluginpath.h",
+ "include/imagecache.h",
+ "imagecache/imagecachecollector.cpp",
+ "imagecache/imagecachecollector.h",
+ "imagecache/imagecache.cpp",
+ "imagecache/imagecachecollectorinterface.h",
+ "imagecache/imagecacheconnectionmanager.cpp",
+ "imagecache/imagecacheconnectionmanager.h",
+ "imagecache/imagecachegeneratorinterface.h",
+ "imagecache/imagecachegenerator.cpp",
+ "imagecache/imagecachegenerator.h",
+ "imagecache/imagecachestorageinterface.h",
+ "imagecache/imagecachestorage.h",
+ "imagecache/timestampproviderinterface.h",
+ "imagecache/timestampprovider.h",
+ "imagecache/timestampprovider.cpp",
]
}
@@ -602,6 +618,8 @@ Project {
"itemlibrary/itemlibrarywidget.h",
"itemlibrary/customfilesystemmodel.cpp",
"itemlibrary/customfilesystemmodel.h",
+ "itemlibrary/itemlibraryiconimageprovider.cpp",
+ "itemlibrary/itemlibraryiconimageprovider.h",
"navigator/iconcheckboxitemdelegate.cpp",
"navigator/iconcheckboxitemdelegate.h",
"navigator/nameitemdelegate.cpp",
diff --git a/src/plugins/qmldesigner/qmldesignerunittestfiles.pri b/src/plugins/qmldesigner/qmldesignerunittestfiles.pri
index cd4d52e8d3..b5ae93f2f1 100644
--- a/src/plugins/qmldesigner/qmldesignerunittestfiles.pri
+++ b/src/plugins/qmldesigner/qmldesignerunittestfiles.pri
@@ -1,5 +1,6 @@
INCLUDEPATH += $$PWD
INCLUDEPATH += $$PWD/designercore/include
+INCLUDEPATH += $$PWD/designercore/imagecache
INCLUDEPATH += $$PWD/designercore
INCLUDEPATH += $$PWD/../../../share/qtcreator/qml/qmlpuppet/interfaces
INCLUDEPATH += $$PWD/../../../share/qtcreator/qml/qmlpuppet/types
@@ -30,9 +31,18 @@ SOURCES += \
$$PWD/designercore/model/variantproperty.cpp\
$$PWD/designercore/model/annotation.cpp \
$$PWD/designercore/rewritertransaction.cpp \
- $$PWD/components/listmodeleditor/listmodeleditormodel.cpp
+ $$PWD/components/listmodeleditor/listmodeleditormodel.cpp \
+ $$PWD/designercore/imagecache/imagecache.cpp \
+ $$PWD/designercore/imagecache/imagecachegenerator.cpp
HEADERS += \
+ $$PWD/designercore/imagecache/imagecachecollectorinterface.h \
+ $$PWD/designercore/imagecache/imagecachestorage.h \
+ $$PWD/designercore/imagecache/imagecachegenerator.h \
+ $$PWD/designercore/imagecache/imagecachestorageinterface.h \
+ $$PWD/designercore/imagecache/imagecachegeneratorinterface.h \
+ $$PWD/designercore/imagecache/timestampproviderinterface.h \
+ $$PWD/designercore/include/imagecache.h \
$$PWD/designercore/include/modelnode.h \
$$PWD/designercore/include/model.h \
$$PWD/../../../share/qtcreator/qml/qmlpuppet/interfaces/commondefines.h \
diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt
index 3d2e53a48c..85809020d8 100644
--- a/src/tools/qml2puppet/CMakeLists.txt
+++ b/src/tools/qml2puppet/CMakeLists.txt
@@ -174,6 +174,7 @@ extend_qtc_executable(qml2puppet
qt5capturepreviewnodeinstanceserver.cpp qt5capturepreviewnodeinstanceserver.h
nodeinstanceserverdispatcher.cpp nodeinstanceserverdispatcher.h
capturenodeinstanceserverdispatcher.cpp capturenodeinstanceserverdispatcher.h
+ qt5captureimagenodeinstanceserver.cpp qt5captureimagenodeinstanceserver.h
)
extend_qtc_executable(qml2puppet
diff --git a/src/tools/qml2puppet/qml2puppet.qbs b/src/tools/qml2puppet/qml2puppet.qbs
index f38a6c8215..742c20406e 100644
--- a/src/tools/qml2puppet/qml2puppet.qbs
+++ b/src/tools/qml2puppet/qml2puppet.qbs
@@ -224,6 +224,8 @@ QtcTool {
"instances/servernodeinstance.h",
"instances/qt5capturepreviewnodeinstanceserver.cpp",
"instances/qt5capturepreviewnodeinstanceserver.h",
+ "instances/qt5captureimagenodeinstanceserver.cpp",
+ "instances/qt5captureimagenodeinstanceserver.h",
"instances/nodeinstanceserverdispatcher.cpp",
"instances/nodeinstanceserverdispatcher.h",
"instances/capturenodeinstanceserverdispatcher.cpp",
diff --git a/tests/unit/mockup/qmldesigner/designercore/include/nodeinstanceview.h b/tests/unit/mockup/qmldesigner/designercore/include/nodeinstanceview.h
index 50d0e22a2d..50dcf529b6 100644
--- a/tests/unit/mockup/qmldesigner/designercore/include/nodeinstanceview.h
+++ b/tests/unit/mockup/qmldesigner/designercore/include/nodeinstanceview.h
@@ -28,6 +28,10 @@
#include "qmldesignercorelib_global.h"
#include "abstractview.h"
+namespace ProjectExplorer {
+class Target;
+}
+
namespace QmlDesigner {
class NodeInstanceView : public AbstractView
@@ -88,6 +92,8 @@ public:
void requestModelNodePreviewImage(const ModelNode &node) {}
void sendToken(const QString &token, int number, const QVector<ModelNode> &nodeVector) {}
+ void setTarget(ProjectExplorer::Target *newTarget) {}
+ void setCrashCallback(std::function<void()>) {}
};
} // namespace QmlDesigner
diff --git a/tests/unit/unittest/CMakeLists.txt b/tests/unit/unittest/CMakeLists.txt
index ff0c1abaad..13741ae478 100644
--- a/tests/unit/unittest/CMakeLists.txt
+++ b/tests/unit/unittest/CMakeLists.txt
@@ -174,6 +174,19 @@ add_qtc_test(unittest GTEST
sqlstatementbuilder-test.cpp
createtablesqlstatementbuilder-test.cpp
sqlitevalue-test.cpp
+ imagecache-test.cpp
+ imagecachegenerator-test.cpp
+ imagecachestorage-test.cpp
+ sqlitedatabasemock.h
+ sqlitereadstatementmock.cpp sqlitereadstatementmock.h
+ sqlitestatementmock.h
+ sqlitetransactionbackendmock.h
+ sqlitewritestatementmock.cpp sqlitewritestatementmock.h
+ notification.h
+ mocktimestampprovider.h
+ imagecachecollectormock.h
+ mockimagecachegenerator.h
+ mockimagecachestorage.h
)
function(extend_qtc_test_with_target_sources target)
@@ -335,6 +348,7 @@ extend_qtc_test(unittest
"${QmlDesignerDir}"
"${QmlDesignerDir}/designercore"
"${QmlDesignerDir}/designercore/include"
+ "${QmlDesignerDir}/designercore/imagecache"
"${QmlDesignerDir}/../../../share/qtcreator/qml/qmlpuppet/interfaces"
"${QmlDesignerDir}/../../../share/qtcreator/qml/qmlpuppet/types"
DEFINES
@@ -382,6 +396,13 @@ extend_qtc_test(unittest
model/signalhandlerproperty.cpp include/signalhandlerproperty.h
model/variantproperty.cpp include/variantproperty.h
rewritertransaction.cpp rewritertransaction.h
+ imagecache/imagecache.cpp include/imagecache.h
+ imagecache/imagecachecollectorinterface.h
+ imagecache/imagecachegenerator.cpp imagecache/imagecachegenerator.h
+ imagecache/imagecachegeneratorinterface.h
+ imagecache/imagecachestorage.h
+ imagecache/imagecachestorageinterface.h
+ imagecache/timestampproviderinterface.h
include/qmldesignercorelib_global.h
diff --git a/tests/unit/unittest/gmock_dependency.pri b/tests/unit/unittest/gmock_dependency.pri
index 1af7b95f09..614c6379d3 100644
--- a/tests/unit/unittest/gmock_dependency.pri
+++ b/tests/unit/unittest/gmock_dependency.pri
@@ -14,10 +14,8 @@ defineTest(setGoogleTestDirectories) {
}
isEmpty(GOOGLETEST_DIR) {
- exists($$PWD/../../../../googletest) {
- setGoogleTestDirectories($$PWD/../../../../googletest)
- } else: exists($$PWD/../../../../../googletest) {
- setGoogleTestDirectories($$PWD/../../../../../googletest)
+ exists($$PWD/3rdparty/googletest) {
+ setGoogleTestDirectories($$PWD/3rdparty/googletest)
} else: linux {
GTEST_INCLUDE_DIR = /usr/include/gtest
GMOCK_INCLUDE_DIR = /usr/include/gmock
diff --git a/tests/unit/unittest/google-using-declarations.h b/tests/unit/unittest/google-using-declarations.h
index 99514fe9b4..4419c8236d 100644
--- a/tests/unit/unittest/google-using-declarations.h
+++ b/tests/unit/unittest/google-using-declarations.h
@@ -35,12 +35,16 @@ using testing::An;
using testing::AnyNumber;
using testing::AnyOf;
using testing::Assign;
+using testing::AtLeast;
+using testing::AtMost;
+using testing::Between;
using testing::ByMove;
using testing::ByRef;
using testing::ContainerEq;
using testing::Contains;
using testing::ElementsAre;
using testing::Eq;
+using testing::Exactly;
using testing::Field;
using testing::Ge;
using testing::Gt;
diff --git a/tests/unit/unittest/gtest-creator-printing.cpp b/tests/unit/unittest/gtest-creator-printing.cpp
index 8d84ad4d2e..c9c871475d 100644
--- a/tests/unit/unittest/gtest-creator-printing.cpp
+++ b/tests/unit/unittest/gtest-creator-printing.cpp
@@ -70,6 +70,7 @@
#include <usedmacro.h>
#include <utils/link.h>
#include <variantproperty.h>
+#include <qmldesigner/designercore/imagecache/imagecachestorageinterface.h>
#include <sqlite3ext.h>
@@ -1468,6 +1469,15 @@ std::ostream &operator<<(std::ostream &out, const VariantProperty &property)
return out << "(" << property.parentModelNode() << ", " << property.name() << ", "
<< property.value() << ")";
}
+
+namespace Internal {
+std::ostream &operator<<(std::ostream &out, const ImageCacheStorageEntry &entry)
+{
+ return out << "(" << entry.image << ", " << entry.hasEntry << ")";
+}
+
+} // namespace Internal
+
} // namespace QmlDesigner
void setFilePathCache(ClangBackEnd::FilePathCaching *cache)
diff --git a/tests/unit/unittest/gtest-creator-printing.h b/tests/unit/unittest/gtest-creator-printing.h
index 565479be03..cdef782578 100644
--- a/tests/unit/unittest/gtest-creator-printing.h
+++ b/tests/unit/unittest/gtest-creator-printing.h
@@ -356,6 +356,13 @@ class VariantProperty;
std::ostream &operator<<(std::ostream &out, const ModelNode &node);
std::ostream &operator<<(std::ostream &out, const VariantProperty &property);
+
+namespace Internal {
+class ImageCacheStorageEntry;
+
+std::ostream &operator<<(std::ostream &out, const ImageCacheStorageEntry &entry);
+
+} // namespace Internal
} // namespace QmlDesigner
void setFilePathCache(ClangBackEnd::FilePathCaching *filePathCache);
diff --git a/tests/unit/unittest/gtest-qt-printing.cpp b/tests/unit/unittest/gtest-qt-printing.cpp
index c097fd0b4c..445ebf7f51 100644
--- a/tests/unit/unittest/gtest-qt-printing.cpp
+++ b/tests/unit/unittest/gtest-qt-printing.cpp
@@ -85,6 +85,11 @@ std::ostream &operator<<(std::ostream &out, const QTextCharFormat &format)
return out;
}
+std::ostream &operator<<(std::ostream &out, const QImage &image)
+{
+ return out << "(" << image.width() << ", " << image.height() << ", " << image.format() << ")";
+}
+
void PrintTo(const QString &text, std::ostream *os)
{
*os << text;
diff --git a/tests/unit/unittest/gtest-qt-printing.h b/tests/unit/unittest/gtest-qt-printing.h
index ebaeb2c785..54db4ee105 100644
--- a/tests/unit/unittest/gtest-qt-printing.h
+++ b/tests/unit/unittest/gtest-qt-printing.h
@@ -34,11 +34,13 @@ QT_BEGIN_NAMESPACE
class QVariant;
class QString;
class QTextCharFormat;
+class QImage;
std::ostream &operator<<(std::ostream &out, const QVariant &QVariant);
std::ostream &operator<<(std::ostream &out, const QString &text);
std::ostream &operator<<(std::ostream &out, const QByteArray &byteArray);
std::ostream &operator<<(std::ostream &out, const QTextCharFormat &format);
+std::ostream &operator<<(std::ostream &out, const QImage &image);
void PrintTo(const QString &text, std::ostream *os);
void PrintTo(const QVariant &variant, std::ostream *os);
diff --git a/tests/unit/unittest/imagecache-test.cpp b/tests/unit/unittest/imagecache-test.cpp
new file mode 100644
index 0000000000..4219335774
--- /dev/null
+++ b/tests/unit/unittest/imagecache-test.cpp
@@ -0,0 +1,326 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#include "googletest.h"
+
+#include "mockimagecachegenerator.h"
+#include "mockimagecachestorage.h"
+#include "mocktimestampprovider.h"
+#include "notification.h"
+
+#include <imagecache.h>
+
+namespace {
+
+class ImageCache : public testing::Test
+{
+protected:
+ Notification notification;
+ Notification waitInThread;
+ NiceMock<MockImageCacheStorage> mockStorage;
+ NiceMock<MockImageCacheGenerator> mockGenerator;
+ NiceMock<MockTimeStampProvider> mockTimeStampProvider;
+ QmlDesigner::ImageCache cache{mockStorage, mockGenerator, mockTimeStampProvider};
+ NiceMock<MockFunction<void()>> mockAbortCallback;
+ NiceMock<MockFunction<void(const QImage &image)>> mockCaptureCallback;
+ QImage image1{10, 10, QImage::Format_ARGB32};
+};
+
+TEST_F(ImageCache, RequestImageFetchesImageFromStorage)
+{
+ EXPECT_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), _))
+ .WillRepeatedly([&](Utils::SmallStringView, auto) {
+ notification.notify();
+ return QmlDesigner::ImageCacheStorageInterface::Entry{{}, false};
+ });
+
+ cache.requestImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestImageFetchesImageFromStorageWithTimeStamp)
+{
+ EXPECT_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
+ .WillRepeatedly(Return(Sqlite::TimeStamp{123}));
+ EXPECT_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{123})))
+ .WillRepeatedly([&](Utils::SmallStringView, auto) {
+ notification.notify();
+ return QmlDesigner::ImageCacheStorageInterface::Entry{QImage{}, false};
+ });
+
+ cache.requestImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestImageCallsCaptureCallbackWithImageFromStorage)
+{
+ ON_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), _))
+ .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::Entry{image1, true}));
+
+ EXPECT_CALL(mockCaptureCallback, Call(Eq(image1))).WillRepeatedly([&](const QImage &) {
+ notification.notify();
+ });
+
+ cache.requestImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestImageCallsAbortCallbackWithoutImage)
+{
+ ON_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), _))
+ .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::Entry{QImage{}, true}));
+
+ EXPECT_CALL(mockAbortCallback, Call()).WillRepeatedly([&] { notification.notify(); });
+
+ cache.requestImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestImageRequestImageFromGenerator)
+{
+ ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
+ .WillByDefault(Return(Sqlite::TimeStamp{123}));
+
+ EXPECT_CALL(mockGenerator,
+ generateImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{123}), _, _))
+ .WillRepeatedly([&](auto &&, auto, auto &&callback, auto) { notification.notify(); });
+
+ cache.requestImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestImageCallsCaptureCallbackWithImageFromGenerator)
+{
+ ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _))
+ .WillByDefault([&](auto &&, auto, auto &&callback, auto) {
+ callback(QImage{image1});
+ notification.notify();
+ });
+
+ EXPECT_CALL(mockCaptureCallback, Call(Eq(image1)));
+
+ cache.requestImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestImageCallsAbortCallbackFromGenerator)
+{
+ ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _))
+ .WillByDefault([&](auto &&, auto, auto &&, auto &&abortCallback) {
+ abortCallback();
+ notification.notify();
+ });
+
+ EXPECT_CALL(mockAbortCallback, Call());
+
+ cache.requestImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestIconFetchesIconFromStorage)
+{
+ EXPECT_CALL(mockStorage, fetchIcon(Eq("/path/to/Component.qml"), _))
+ .WillRepeatedly([&](Utils::SmallStringView, auto) {
+ notification.notify();
+ return QmlDesigner::ImageCacheStorageInterface::Entry{{}, false};
+ });
+
+ cache.requestIcon("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestIconFetchesIconFromStorageWithTimeStamp)
+{
+ EXPECT_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
+ .WillRepeatedly(Return(Sqlite::TimeStamp{123}));
+ EXPECT_CALL(mockStorage, fetchIcon(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{123})))
+ .WillRepeatedly([&](Utils::SmallStringView, auto) {
+ notification.notify();
+ return QmlDesigner::ImageCacheStorageInterface::Entry{QImage{}, false};
+ });
+
+ cache.requestIcon("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestIconCallsCaptureCallbackWithImageFromStorage)
+{
+ ON_CALL(mockStorage, fetchIcon(Eq("/path/to/Component.qml"), _))
+ .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::Entry{image1, true}));
+
+ EXPECT_CALL(mockCaptureCallback, Call(Eq(image1))).WillRepeatedly([&](const QImage &) {
+ notification.notify();
+ });
+
+ cache.requestIcon("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestIconCallsAbortCallbackWithoutIcon)
+{
+ ON_CALL(mockStorage, fetchIcon(Eq("/path/to/Component.qml"), _))
+ .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::Entry{QImage{}, true}));
+
+ EXPECT_CALL(mockAbortCallback, Call()).WillRepeatedly([&] { notification.notify(); });
+
+ cache.requestIcon("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestIconRequestImageFromGenerator)
+{
+ ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
+ .WillByDefault(Return(Sqlite::TimeStamp{123}));
+
+ EXPECT_CALL(mockGenerator,
+ generateImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{123}), _, _))
+ .WillRepeatedly([&](auto &&, auto, auto &&callback, auto) { notification.notify(); });
+
+ cache.requestIcon("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestIconCallsCaptureCallbackWithImageFromGenerator)
+{
+ ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _))
+ .WillByDefault([&](auto &&, auto, auto &&callback, auto) {
+ callback(QImage{image1});
+ notification.notify();
+ });
+
+ EXPECT_CALL(mockCaptureCallback, Call(Eq(image1)));
+
+ cache.requestIcon("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestIconCallsAbortCallbackFromGenerator)
+{
+ ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _))
+ .WillByDefault([&](auto &&, auto, auto &&, auto &&abortCallback) {
+ abortCallback();
+ notification.notify();
+ });
+
+ EXPECT_CALL(mockAbortCallback, Call());
+
+ cache.requestIcon("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, CleanRemovesEntries)
+{
+ EXPECT_CALL(mockGenerator, generateImage(Eq("/path/to/Component1.qml"), _, _, _))
+ .WillRepeatedly([&](auto &&, auto, auto &&mockCaptureCallback, auto &&) {
+ mockCaptureCallback(QImage{});
+ waitInThread.wait();
+ });
+ EXPECT_CALL(mockGenerator, generateImage(_, _, _, _))
+ .WillRepeatedly([&](auto &&, auto, auto &&mockCaptureCallback, auto &&) {
+ mockCaptureCallback(QImage{});
+ });
+ cache.requestIcon("/path/to/Component1.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+
+ EXPECT_CALL(mockCaptureCallback, Call(_)).Times(AtMost(1));
+
+ cache.requestIcon("/path/to/Component3.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ cache.clean();
+ waitInThread.notify();
+}
+
+TEST_F(ImageCache, CleanCallsAbort)
+{
+ ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component1.qml"), _, _, _))
+ .WillByDefault(
+ [&](auto &&, auto, auto &&mockCaptureCallback, auto &&) { waitInThread.wait(); });
+ cache.requestIcon("/path/to/Component1.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ cache.requestIcon("/path/to/Component2.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+
+ EXPECT_CALL(mockAbortCallback, Call()).Times(AtLeast(2));
+
+ cache.requestIcon("/path/to/Component3.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ cache.clean();
+ waitInThread.notify();
+}
+
+TEST_F(ImageCache, CleanCallsGeneratorClean)
+{
+ EXPECT_CALL(mockGenerator, clean()).Times(AtLeast(1));
+
+ cache.clean();
+}
+
+TEST_F(ImageCache, AfterCleanNewJobsWorks)
+{
+ cache.clean();
+
+ EXPECT_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _))
+ .WillRepeatedly([&](auto &&, auto, auto &&, auto &&) { notification.notify(); });
+
+ cache.requestIcon("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+} // namespace
diff --git a/tests/unit/unittest/imagecachecollectormock.h b/tests/unit/unittest/imagecachecollectormock.h
new file mode 100644
index 0000000000..93520c418d
--- /dev/null
+++ b/tests/unit/unittest/imagecachecollectormock.h
@@ -0,0 +1,41 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "googletest.h"
+
+#include <imagecachecollectorinterface.h>
+
+class ImageCacheCollectorMock : public QmlDesigner::ImageCacheCollectorInterface
+{
+public:
+ MOCK_METHOD(void,
+ start,
+ (Utils::SmallStringView filePath,
+ ImageCacheCollectorInterface::CaptureCallback captureCallback,
+ ImageCacheCollectorInterface::AbortCallback abortCallback),
+ (override));
+};
diff --git a/tests/unit/unittest/imagecachegenerator-test.cpp b/tests/unit/unittest/imagecachegenerator-test.cpp
new file mode 100644
index 0000000000..f152bd83ad
--- /dev/null
+++ b/tests/unit/unittest/imagecachegenerator-test.cpp
@@ -0,0 +1,240 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#include "googletest.h"
+
+#include "imagecachecollectormock.h"
+#include "mockimagecachestorage.h"
+#include "notification.h"
+
+#include <imagecachegenerator.h>
+
+#include <mutex>
+
+namespace {
+
+class ImageCacheGenerator : public testing::Test
+{
+protected:
+ template<typename Callable, typename... Arguments>
+ static void executeAsync(Callable &&call, Arguments... arguments)
+ {
+ std::thread thread(
+ [](Callable &&call, Arguments... arguments) {
+ call(std::forward<Arguments>(arguments)...);
+ },
+ std::forward<Callable>(call),
+ std::forward<Arguments>(arguments)...);
+ thread.detach();
+ }
+
+protected:
+ Notification waitInThread;
+ Notification notification;
+ QImage image1{10, 10, QImage::Format_ARGB32};
+ NiceMock<MockFunction<void(const QImage &)>> imageCallbackMock;
+ NiceMock<MockFunction<void()>> abortCallbackMock;
+ NiceMock<ImageCacheCollectorMock> collectorMock;
+ NiceMock<MockImageCacheStorage> storageMock;
+ QmlDesigner::ImageCacheGenerator generator{collectorMock, storageMock};
+};
+
+TEST_F(ImageCacheGenerator, CallsCollectorWithCaptureCallback)
+{
+ EXPECT_CALL(collectorMock, start(Eq("name"), _, _))
+ .WillRepeatedly([&](auto, auto captureCallback, auto) { captureCallback(QImage{image1}); });
+ EXPECT_CALL(imageCallbackMock, Call(_)).WillRepeatedly([&](const QImage &) {
+ notification.notify();
+ });
+
+ generator.generateImage("name", {}, imageCallbackMock.AsStdFunction(), {});
+ notification.wait();
+}
+
+TEST_F(ImageCacheGenerator, CallsCollectorOnlyIfNotProcessing)
+{
+ EXPECT_CALL(collectorMock, start(Eq("name"), _, _)).WillRepeatedly([&](auto, auto, auto) {
+ notification.notify();
+ });
+
+ generator.generateImage("name", {}, imageCallbackMock.AsStdFunction(), {});
+ generator.generateImage("name", {}, imageCallbackMock.AsStdFunction(), {});
+ notification.wait(2);
+}
+
+TEST_F(ImageCacheGenerator, ProcessTaskAfterFirstFinished)
+{
+ ON_CALL(imageCallbackMock, Call(_)).WillByDefault([&](const QImage &) { notification.notify(); });
+
+ EXPECT_CALL(collectorMock, start(Eq("name"), _, _)).WillOnce([&](auto, auto captureCallback, auto) {
+ captureCallback(QImage{image1});
+ });
+ EXPECT_CALL(collectorMock, start(Eq("name2"), _, _)).WillOnce([&](auto, auto captureCallback, auto) {
+ captureCallback(QImage{image1});
+ });
+
+ generator.generateImage("name", {}, imageCallbackMock.AsStdFunction(), {});
+ generator.generateImage("name2", {}, imageCallbackMock.AsStdFunction(), {});
+ notification.wait(2);
+}
+
+TEST_F(ImageCacheGenerator, DontCrashAtDestructingGenerator)
+{
+ ON_CALL(collectorMock, start(Eq("name"), _, _)).WillByDefault([&](auto, auto captureCallback, auto) {
+ captureCallback(QImage{image1});
+ });
+
+ generator.generateImage("name", {}, imageCallbackMock.AsStdFunction(), {});
+ generator.generateImage("name2", {}, imageCallbackMock.AsStdFunction(), {});
+ generator.generateImage("name3", {}, imageCallbackMock.AsStdFunction(), {});
+ generator.generateImage("name4", {}, imageCallbackMock.AsStdFunction(), {});
+}
+
+TEST_F(ImageCacheGenerator, StoreImage)
+{
+ ON_CALL(collectorMock, start(Eq("name"), _, _)).WillByDefault([&](auto, auto captureCallback, auto) {
+ captureCallback(QImage{image1});
+ });
+
+ EXPECT_CALL(storageMock, storeImage(Eq("name"), Eq(Sqlite::TimeStamp{11}), Eq(image1)))
+ .WillRepeatedly([&](auto, auto, auto) { notification.notify(); });
+
+ generator.generateImage("name", {11}, imageCallbackMock.AsStdFunction(), {});
+ notification.wait();
+}
+
+TEST_F(ImageCacheGenerator, StoreNullImage)
+{
+ ON_CALL(collectorMock, start(Eq("name"), _, _)).WillByDefault([&](auto, auto captureCallback, auto) {
+ captureCallback(QImage{});
+ });
+
+ EXPECT_CALL(storageMock, storeImage(Eq("name"), Eq(Sqlite::TimeStamp{11}), Eq(QImage{})))
+ .WillRepeatedly([&](auto, auto, auto) { notification.notify(); });
+
+ generator.generateImage("name",
+ {11},
+ imageCallbackMock.AsStdFunction(),
+ abortCallbackMock.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCacheGenerator, AbortCallback)
+{
+ ON_CALL(collectorMock, start(Eq("name"), _, _)).WillByDefault([&](auto, auto captureCallback, auto) {
+ captureCallback(QImage{image1});
+ });
+ ON_CALL(collectorMock, start(Eq("name2"), _, _)).WillByDefault([&](auto, auto, auto abortCallback) {
+ abortCallback();
+ });
+
+ EXPECT_CALL(imageCallbackMock, Call(_)).WillOnce([&](const QImage &) { notification.notify(); });
+ EXPECT_CALL(abortCallbackMock, Call()).WillOnce([&]() { notification.notify(); });
+
+ generator.generateImage("name",
+ {},
+ imageCallbackMock.AsStdFunction(),
+ abortCallbackMock.AsStdFunction());
+ generator.generateImage("name2",
+ {},
+ imageCallbackMock.AsStdFunction(),
+ abortCallbackMock.AsStdFunction());
+ notification.wait(2);
+}
+
+TEST_F(ImageCacheGenerator, StoreNullImageForAbortCallback)
+{
+ ON_CALL(collectorMock, start(_, _, _)).WillByDefault([&](auto, auto, auto abortCallback) {
+ abortCallback();
+ notification.notify();
+ });
+
+ EXPECT_CALL(abortCallbackMock, Call()).WillOnce([&]() { notification.notify(); });
+ EXPECT_CALL(storageMock, storeImage(Eq("name"), Eq(Sqlite::TimeStamp{11}), Eq(QImage{})));
+
+ generator.generateImage("name",
+ {11},
+ imageCallbackMock.AsStdFunction(),
+ abortCallbackMock.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCacheGenerator, AbortForEmptyImage)
+{
+ NiceMock<MockFunction<void()>> abortCallbackMock;
+ ON_CALL(collectorMock, start(Eq("name"), _, _)).WillByDefault([&](auto, auto captureCallback, auto) {
+ captureCallback(QImage{});
+ });
+
+ EXPECT_CALL(abortCallbackMock, Call()).WillOnce([&]() { notification.notify(); });
+
+ generator.generateImage("name",
+ {},
+ imageCallbackMock.AsStdFunction(),
+ abortCallbackMock.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCacheGenerator, CallWalCheckpointFullIfQueueIsEmpty)
+{
+ ON_CALL(collectorMock, start(Eq("name"), _, _)).WillByDefault([&](auto, auto captureCallback, auto) {
+ captureCallback({});
+ });
+
+ EXPECT_CALL(storageMock, walCheckpointFull()).WillRepeatedly([&]() { notification.notify(); });
+
+ generator.generateImage("name",
+ {11},
+ imageCallbackMock.AsStdFunction(),
+ abortCallbackMock.AsStdFunction());
+ generator.generateImage("name2",
+ {11},
+ imageCallbackMock.AsStdFunction(),
+ abortCallbackMock.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCacheGenerator, Clean)
+{
+ ON_CALL(collectorMock, start(_, _, _)).WillByDefault([&](auto, auto captureCallback, auto) {
+ captureCallback({});
+ waitInThread.wait();
+ });
+ generator.generateImage("name",
+ {11},
+ imageCallbackMock.AsStdFunction(),
+ abortCallbackMock.AsStdFunction());
+ generator.generateImage("name2",
+ {11},
+ imageCallbackMock.AsStdFunction(),
+ abortCallbackMock.AsStdFunction());
+
+ EXPECT_CALL(imageCallbackMock, Call(_)).Times(0);
+
+ generator.clean();
+ waitInThread.notify();
+}
+
+} // namespace
diff --git a/tests/unit/unittest/imagecachestorage-test.cpp b/tests/unit/unittest/imagecachestorage-test.cpp
new file mode 100644
index 0000000000..aaf30526b6
--- /dev/null
+++ b/tests/unit/unittest/imagecachestorage-test.cpp
@@ -0,0 +1,334 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#include "googletest.h"
+
+#include "sqlitedatabasemock.h"
+
+#include <imagecachestorage.h>
+#include <sqlitedatabase.h>
+
+namespace {
+
+MATCHER_P2(IsEntry,
+ image,
+ hasEntry,
+ std::string(negation ? "is't" : "is")
+ + PrintToString(QmlDesigner::ImageCacheStorageInterface::Entry{image, hasEntry}))
+{
+ const QmlDesigner::ImageCacheStorageInterface::Entry &entry = arg;
+ return entry.image == image && entry.hasEntry == hasEntry;
+}
+
+class ImageCacheStorageTest : public testing::Test
+{
+protected:
+ QImage createImage()
+ {
+ QImage image{150, 150, QImage::Format_ARGB32};
+ image.fill(QColor{128, 64, 0, 11});
+ image.setPixelColor(75, 75, QColor{1, 255, 33, 196});
+
+ return image;
+ }
+
+protected:
+ using ReadStatement = QmlDesigner::ImageCacheStorage<SqliteDatabaseMock>::ReadStatement;
+ using WriteStatement = QmlDesigner::ImageCacheStorage<SqliteDatabaseMock>::WriteStatement;
+
+ NiceMock<SqliteDatabaseMock> databaseMock;
+ QmlDesigner::ImageCacheStorage<SqliteDatabaseMock> storage{databaseMock};
+ ReadStatement &selectImageStatement = storage.selectImageStatement;
+ ReadStatement &selectIconStatement = storage.selectIconStatement;
+ WriteStatement &upsertImageStatement = storage.upsertImageStatement;
+ QImage image1{createImage()};
+};
+
+TEST_F(ImageCacheStorageTest, Initialize)
+{
+ InSequence s;
+
+ EXPECT_CALL(databaseMock, exclusiveBegin());
+ EXPECT_CALL(databaseMock,
+ execute(Eq("CREATE TABLE IF NOT EXISTS images(id INTEGER PRIMARY KEY, name TEXT "
+ "NOT NULL UNIQUE, mtime INTEGER, image BLOB, icon BLOB)")));
+ EXPECT_CALL(databaseMock, commit());
+ EXPECT_CALL(databaseMock, immediateBegin());
+ EXPECT_CALL(databaseMock, prepare(Eq(selectImageStatement.sqlStatement)));
+ EXPECT_CALL(databaseMock, prepare(Eq(selectIconStatement.sqlStatement)));
+ EXPECT_CALL(databaseMock, prepare(Eq(upsertImageStatement.sqlStatement)));
+ EXPECT_CALL(databaseMock, commit());
+
+ QmlDesigner::ImageCacheStorage<SqliteDatabaseMock> storage{databaseMock};
+}
+
+TEST_F(ImageCacheStorageTest, FetchImageCalls)
+{
+ InSequence s;
+
+ EXPECT_CALL(databaseMock, deferredBegin());
+ EXPECT_CALL(selectImageStatement,
+ valueReturnBlob(TypedEq<Utils::SmallStringView>("/path/to/component"),
+ TypedEq<long long>(123)));
+ EXPECT_CALL(databaseMock, commit());
+
+ storage.fetchImage("/path/to/component", {123});
+}
+
+TEST_F(ImageCacheStorageTest, FetchImageCallsIsBusy)
+{
+ InSequence s;
+
+ EXPECT_CALL(databaseMock, deferredBegin());
+ EXPECT_CALL(selectImageStatement,
+ valueReturnBlob(TypedEq<Utils::SmallStringView>("/path/to/component"),
+ TypedEq<long long>(123)))
+ .WillOnce(Throw(Sqlite::StatementIsBusy("busy")));
+ EXPECT_CALL(databaseMock, rollback());
+ EXPECT_CALL(databaseMock, deferredBegin());
+ EXPECT_CALL(selectImageStatement,
+ valueReturnBlob(TypedEq<Utils::SmallStringView>("/path/to/component"),
+ TypedEq<long long>(123)));
+ EXPECT_CALL(databaseMock, commit());
+
+ storage.fetchImage("/path/to/component", {123});
+}
+
+TEST_F(ImageCacheStorageTest, FetchIconCalls)
+{
+ InSequence s;
+
+ EXPECT_CALL(databaseMock, deferredBegin());
+ EXPECT_CALL(selectIconStatement,
+ valueReturnBlob(TypedEq<Utils::SmallStringView>("/path/to/component"),
+ TypedEq<long long>(123)));
+ EXPECT_CALL(databaseMock, commit());
+
+ storage.fetchIcon("/path/to/component", {123});
+}
+
+TEST_F(ImageCacheStorageTest, FetchIconCallsIsBusy)
+{
+ InSequence s;
+
+ EXPECT_CALL(databaseMock, deferredBegin());
+ EXPECT_CALL(selectIconStatement,
+ valueReturnBlob(TypedEq<Utils::SmallStringView>("/path/to/component"),
+ TypedEq<long long>(123)))
+ .WillOnce(Throw(Sqlite::StatementIsBusy("busy")));
+ EXPECT_CALL(databaseMock, rollback());
+ EXPECT_CALL(databaseMock, deferredBegin());
+ EXPECT_CALL(selectIconStatement,
+ valueReturnBlob(TypedEq<Utils::SmallStringView>("/path/to/component"),
+ TypedEq<long long>(123)));
+ EXPECT_CALL(databaseMock, commit());
+
+ storage.fetchIcon("/path/to/component", {123});
+}
+
+TEST_F(ImageCacheStorageTest, StoreImageCalls)
+{
+ InSequence s;
+
+ EXPECT_CALL(databaseMock, immediateBegin());
+ EXPECT_CALL(upsertImageStatement,
+ write(TypedEq<Utils::SmallStringView>("/path/to/component"),
+ TypedEq<long long>(123),
+ A<Sqlite::BlobView>(),
+ A<Sqlite::BlobView>()));
+ EXPECT_CALL(databaseMock, commit());
+
+ storage.storeImage("/path/to/component", {123}, image1);
+}
+
+TEST_F(ImageCacheStorageTest, StoreEmptyImageCalls)
+{
+ InSequence s;
+
+ EXPECT_CALL(databaseMock, immediateBegin());
+ EXPECT_CALL(upsertImageStatement,
+ write(TypedEq<Utils::SmallStringView>("/path/to/component"),
+ TypedEq<long long>(123),
+ A<Sqlite::NullValue>(),
+ A<Sqlite::NullValue>()));
+ EXPECT_CALL(databaseMock, commit());
+
+ storage.storeImage("/path/to/component", {123}, QImage{});
+}
+
+TEST_F(ImageCacheStorageTest, StoreImageCallsIsBusy)
+{
+ InSequence s;
+
+ EXPECT_CALL(databaseMock, immediateBegin()).WillOnce(Throw(Sqlite::StatementIsBusy("busy")));
+ EXPECT_CALL(databaseMock, immediateBegin());
+ EXPECT_CALL(upsertImageStatement,
+ write(TypedEq<Utils::SmallStringView>("/path/to/component"),
+ TypedEq<long long>(123),
+ A<Sqlite::NullValue>(),
+ A<Sqlite::NullValue>()));
+ EXPECT_CALL(databaseMock, commit());
+
+ storage.storeImage("/path/to/component", {123}, QImage{});
+}
+
+TEST_F(ImageCacheStorageTest, CallWalCheckointFull)
+{
+ EXPECT_CALL(databaseMock, walCheckpointFull());
+
+ storage.walCheckpointFull();
+}
+
+TEST_F(ImageCacheStorageTest, CallWalCheckointFullIsBusy)
+{
+ InSequence s;
+
+ EXPECT_CALL(databaseMock, walCheckpointFull()).WillOnce(Throw(Sqlite::StatementIsBusy("busy")));
+ EXPECT_CALL(databaseMock, walCheckpointFull());
+
+ storage.walCheckpointFull();
+}
+
+class ImageCacheStorageSlowTest : public testing::Test
+{
+protected:
+ QImage createImage()
+ {
+ QImage image{150, 150, QImage::Format_ARGB32};
+ image.fill(QColor{128, 64, 0, 11});
+ image.setPixelColor(1, 1, QColor{1, 255, 33, 196});
+
+ return image;
+ }
+
+protected:
+ Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory};
+ QmlDesigner::ImageCacheStorage<Sqlite::Database> storage{database};
+ QImage image1{createImage()};
+ QImage image2{10, 10, QImage::Format_ARGB32};
+ QImage icon1{image1.scaled(96, 96)};
+};
+
+TEST_F(ImageCacheStorageSlowTest, StoreImage)
+{
+ storage.storeImage("/path/to/component", {123}, image1);
+
+ ASSERT_THAT(storage.fetchImage("/path/to/component", {123}), IsEntry(image1, true));
+}
+
+TEST_F(ImageCacheStorageSlowTest, StoreEmptyImageAfterEntry)
+{
+ storage.storeImage("/path/to/component", {123}, image1);
+
+ storage.storeImage("/path/to/component", {123}, QImage{});
+
+ ASSERT_THAT(storage.fetchImage("/path/to/component", {123}), IsEntry(QImage{}, true));
+}
+
+TEST_F(ImageCacheStorageSlowTest, StoreEmptyEntry)
+{
+ storage.storeImage("/path/to/component", {123}, QImage{});
+
+ ASSERT_THAT(storage.fetchImage("/path/to/component", {123}), IsEntry(QImage{}, true));
+}
+
+TEST_F(ImageCacheStorageSlowTest, FetchNonExistingImageIsEmpty)
+{
+ auto image = storage.fetchImage("/path/to/component", {123});
+
+ ASSERT_THAT(image, IsEntry(QImage{}, false));
+}
+
+TEST_F(ImageCacheStorageSlowTest, FetchSameTimeImage)
+{
+ storage.storeImage("/path/to/component", {123}, image1);
+
+ auto image = storage.fetchImage("/path/to/component", {123});
+
+ ASSERT_THAT(image, IsEntry(image1, true));
+}
+
+TEST_F(ImageCacheStorageSlowTest, DoNotFetchOlderImage)
+{
+ storage.storeImage("/path/to/component", {123}, image1);
+
+ auto image = storage.fetchImage("/path/to/component", {124});
+
+ ASSERT_THAT(image, IsEntry(QImage{}, false));
+}
+
+TEST_F(ImageCacheStorageSlowTest, FetchNewerImage)
+{
+ storage.storeImage("/path/to/component", {123}, image1);
+
+ auto image = storage.fetchImage("/path/to/component", {122});
+
+ ASSERT_THAT(image, IsEntry(image1, true));
+}
+
+TEST_F(ImageCacheStorageSlowTest, FetchNonExistingIconIsEmpty)
+{
+ auto image = storage.fetchIcon("/path/to/component", {123});
+
+ ASSERT_THAT(image, IsEntry(QImage{}, false));
+}
+
+TEST_F(ImageCacheStorageSlowTest, FetchSameTimeIcon)
+{
+ storage.storeImage("/path/to/component", {123}, image1);
+
+ auto image = storage.fetchIcon("/path/to/component", {123});
+
+ ASSERT_THAT(image, IsEntry(icon1, true));
+}
+
+TEST_F(ImageCacheStorageSlowTest, DoNotFetchOlderIcon)
+{
+ storage.storeImage("/path/to/component", {123}, image1);
+
+ auto image = storage.fetchIcon("/path/to/component", {124});
+
+ ASSERT_THAT(image, IsEntry(QImage{}, false));
+}
+
+TEST_F(ImageCacheStorageSlowTest, FetchNewerIcon)
+{
+ storage.storeImage("/path/to/component", {123}, image1);
+
+ auto image = storage.fetchIcon("/path/to/component", {122});
+
+ ASSERT_THAT(image, IsEntry(icon1, true));
+}
+
+TEST_F(ImageCacheStorageSlowTest, DontScaleSmallerIcon)
+{
+ storage.storeImage("/path/to/component", {123}, image2);
+
+ auto image = storage.fetchImage("/path/to/component", {122});
+
+ ASSERT_THAT(image, IsEntry(image2, true));
+}
+
+} // namespace
diff --git a/tests/unit/unittest/mockimagecachegenerator.h b/tests/unit/unittest/mockimagecachegenerator.h
new file mode 100644
index 0000000000..ffe8d9c709
--- /dev/null
+++ b/tests/unit/unittest/mockimagecachegenerator.h
@@ -0,0 +1,43 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "googletest.h"
+
+#include <imagecachegeneratorinterface.h>
+
+class MockImageCacheGenerator : public QmlDesigner::ImageCacheGeneratorInterface
+{
+public:
+ MOCK_METHOD(void,
+ generateImage,
+ (Utils::SmallStringView name,
+ Sqlite::TimeStamp timeStamp,
+ CaptureCallback &&captureCallback,
+ AbortCallback &&abortCallback),
+ (override));
+ MOCK_METHOD(void, clean, (), (override));
+};
diff --git a/tests/unit/unittest/mockimagecachestorage.h b/tests/unit/unittest/mockimagecachestorage.h
new file mode 100644
index 0000000000..add2cfaa96
--- /dev/null
+++ b/tests/unit/unittest/mockimagecachestorage.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "googletest.h"
+
+#include <imagecachestorageinterface.h>
+
+class MockImageCacheStorage : public QmlDesigner::ImageCacheStorageInterface
+{
+public:
+ MOCK_METHOD(QmlDesigner::ImageCacheStorageInterface::Entry,
+ fetchImage,
+ (Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp),
+ (const, override));
+
+ MOCK_METHOD(QmlDesigner::ImageCacheStorageInterface::Entry,
+ fetchIcon,
+ (Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp),
+ (const, override));
+
+ MOCK_METHOD(void,
+ storeImage,
+ (Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QImage &image),
+ (override));
+ MOCK_METHOD(void, walCheckpointFull, (), (override));
+};
diff --git a/tests/unit/unittest/mocktimestampprovider.h b/tests/unit/unittest/mocktimestampprovider.h
new file mode 100644
index 0000000000..0adad4c030
--- /dev/null
+++ b/tests/unit/unittest/mocktimestampprovider.h
@@ -0,0 +1,36 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "googletest.h"
+
+#include <timestampproviderinterface.h>
+
+class MockTimeStampProvider : public QmlDesigner::TimeStampProviderInterface
+{
+public:
+ MOCK_METHOD(Sqlite::TimeStamp, timeStamp, (Utils::SmallStringView name), (const, override));
+};
diff --git a/tests/unit/unittest/notification.h b/tests/unit/unittest/notification.h
new file mode 100644
index 0000000000..a27d9acb76
--- /dev/null
+++ b/tests/unit/unittest/notification.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#include "googletest.h"
+
+#include <condition_variable>
+#include <mutex>
+
+class Notification
+{
+public:
+ void wait(int count = 1)
+ {
+ std::unique_lock<std::mutex> lock{m_mutex};
+ m_waitCount += count;
+ if (m_waitCount > 0)
+ m_condition.wait(lock, [&] { return m_waitCount <= 0; });
+ }
+
+ void notify()
+ {
+ {
+ std::unique_lock<std::mutex> lock{m_mutex};
+ --m_waitCount;
+ }
+
+ m_condition.notify_all();
+ }
+
+private:
+ std::mutex m_mutex;
+ std::condition_variable m_condition;
+ int m_waitCount = 0;
+};
diff --git a/tests/unit/unittest/sqlitedatabasemock.h b/tests/unit/unittest/sqlitedatabasemock.h
new file mode 100644
index 0000000000..8c1179e424
--- /dev/null
+++ b/tests/unit/unittest/sqlitedatabasemock.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "googletest.h"
+
+#include "sqlitereadstatementmock.h"
+#include "sqlitetransactionbackendmock.h"
+#include "sqlitewritestatementmock.h"
+
+#include <sqlitedatabaseinterface.h>
+#include <sqlitetable.h>
+#include <sqlitetransaction.h>
+
+#include <utils/smallstringview.h>
+
+class SqliteDatabaseMock : public SqliteTransactionBackendMock, public Sqlite::DatabaseInterface
+{
+public:
+ using ReadStatement = NiceMock<SqliteReadStatementMock>;
+ using WriteStatement = NiceMock<SqliteWriteStatementMock>;
+
+ MOCK_METHOD(void, prepare, (Utils::SmallStringView sqlStatement), ());
+
+ MOCK_METHOD(void, execute, (Utils::SmallStringView sqlStatement), ());
+
+ MOCK_METHOD(int64_t, lastInsertedRowId, (), (const));
+
+ MOCK_METHOD(void, setLastInsertedRowId, (int64_t), (const));
+
+ MOCK_METHOD(bool, isInitialized, (), (const));
+
+ MOCK_METHOD(void, setIsInitialized, (bool), ());
+
+ MOCK_METHOD(void, walCheckpointFull, (), (override));
+
+ MOCK_METHOD(void,
+ setUpdateHook,
+ (void *object,
+ void (*)(void *object, int, char const *database, char const *, long long rowId)),
+ (override));
+
+ MOCK_METHOD(void, resetUpdateHook, (), (override));
+
+ MOCK_METHOD(void, applyAndUpdateSessions, (), (override));
+
+ MOCK_METHOD(void, setAttachedTables, (const Utils::SmallStringVector &tables), (override));
+};
+
diff --git a/tests/unit/unittest/sqlitereadstatementmock.cpp b/tests/unit/unittest/sqlitereadstatementmock.cpp
new file mode 100644
index 0000000000..e3e22d4e16
--- /dev/null
+++ b/tests/unit/unittest/sqlitereadstatementmock.cpp
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#include "sqlitereadstatementmock.h"
+
+#include "sqlitedatabasemock.h"
+
+SqliteReadStatementMock::SqliteReadStatementMock(Utils::SmallStringView sqlStatement,
+ SqliteDatabaseMock &databaseMock)
+ : sqlStatement(sqlStatement)
+{
+ databaseMock.prepare(sqlStatement);
+}
+
+template<>
+std::vector<Utils::SmallString> SqliteReadStatementMock::values<Utils::SmallString>(std::size_t reserveSize)
+{
+ return valuesReturnStringVector(reserveSize);
+}
+
+template<>
+std::vector<long long> SqliteReadStatementMock::values<long long>(std::size_t reserveSize)
+{
+ return valuesReturnRowIds(reserveSize);
+}
+
+template<>
+Utils::optional<long long> SqliteReadStatementMock::value<long long>()
+{
+ return valueReturnLongLong();
+}
+
+template<>
+Utils::optional<Sqlite::ByteArrayBlob> SqliteReadStatementMock::value<Sqlite::ByteArrayBlob>(
+ const Utils::SmallStringView &name, const long long &blob)
+{
+ return valueReturnBlob(name, blob);
+}
diff --git a/tests/unit/unittest/sqlitereadstatementmock.h b/tests/unit/unittest/sqlitereadstatementmock.h
new file mode 100644
index 0000000000..f74cce1e8e
--- /dev/null
+++ b/tests/unit/unittest/sqlitereadstatementmock.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "googletest.h"
+
+#include <sqliteblob.h>
+#include <utils/optional.h>
+#include <utils/smallstring.h>
+
+#include <QImage>
+
+#include <cstdint>
+#include <tuple>
+#include <vector>
+
+class SqliteDatabaseMock;
+
+class SqliteReadStatementMock
+{
+public:
+ SqliteReadStatementMock() = default;
+ SqliteReadStatementMock(Utils::SmallStringView sqlStatement, SqliteDatabaseMock &databaseMock);
+
+ MOCK_METHOD(std::vector<Utils::SmallString>, valuesReturnStringVector, (std::size_t), ());
+
+ MOCK_METHOD(std::vector<long long>, valuesReturnRowIds, (std::size_t), ());
+ MOCK_METHOD(Utils::optional<long long>, valueReturnLongLong, (), ());
+ MOCK_METHOD(Utils::optional<Sqlite::ByteArrayBlob>,
+ valueReturnBlob,
+ (Utils::SmallStringView, long long),
+ ());
+
+ template<typename ResultType, int ResultTypeCount = 1, typename... QueryType>
+ std::vector<ResultType> values(std::size_t reserveSize, const QueryType &... queryValues);
+
+ template <typename ResultType,
+ int ResultTypeCount = 1,
+ typename... QueryType>
+ std::vector<ResultType> values(std::size_t reserveSize);
+
+ template <typename ResultType,
+ int ResultTypeCount = 1,
+ template <typename...> class QueryContainerType,
+ typename QueryElementType>
+ std::vector<ResultType> values(std::size_t reserveSize,
+ const QueryContainerType<QueryElementType> &queryValues);
+
+ template <typename ResultType,
+ int ResultTypeCount = 1,
+ typename... QueryTypes>
+ Utils::optional<ResultType> value(const QueryTypes&... queryValues);
+
+public:
+ Utils::SmallString sqlStatement;
+};
+
+template<>
+std::vector<Utils::SmallString> SqliteReadStatementMock::values<Utils::SmallString>(
+ std::size_t reserveSize);
+
+template<>
+std::vector<long long> SqliteReadStatementMock::values<long long>(std::size_t reserveSize);
+
+template<>
+Utils::optional<long long> SqliteReadStatementMock::value<long long>();
+
+template<>
+Utils::optional<Sqlite::ByteArrayBlob> SqliteReadStatementMock::value<Sqlite::ByteArrayBlob>(
+ const Utils::SmallStringView &name, const long long &blob);
diff --git a/tests/unit/unittest/sqlitestatement-test.cpp b/tests/unit/unittest/sqlitestatement-test.cpp
index 341679a903..19c897f7cb 100644
--- a/tests/unit/unittest/sqlitestatement-test.cpp
+++ b/tests/unit/unittest/sqlitestatement-test.cpp
@@ -27,6 +27,7 @@
#include "mocksqlitestatement.h"
#include "sqliteteststatement.h"
+#include <sqliteblob.h>
#include <sqlitedatabase.h>
#include <sqlitereadstatement.h>
#include <sqlitereadwritestatement.h>
@@ -38,14 +39,6 @@
#include <vector>
-namespace Sqlite {
-bool operator==(Utils::span<const byte> first, Utils::span<const byte> second)
-{
- return first.size() == second.size()
- && std::memcmp(first.data(), second.data(), first.size()) == 0;
-}
-} // namespace Sqlite
-
namespace {
using Sqlite::Database;
@@ -270,7 +263,7 @@ TEST_F(SqliteStatement, BindBlob)
SqliteTestStatement statement("WITH T(blob) AS (VALUES (?)) SELECT blob FROM T", database);
const unsigned char chars[] = "aaafdfdlll";
auto bytePointer = reinterpret_cast<const Sqlite::byte *>(chars);
- Utils::span<const Sqlite::byte> bytes{bytePointer, sizeof(chars) - 1};
+ Sqlite::BlobView bytes{bytePointer, sizeof(chars) - 1};
statement.bind(1, bytes);
statement.next();
@@ -281,7 +274,7 @@ TEST_F(SqliteStatement, BindBlob)
TEST_F(SqliteStatement, BindEmptyBlob)
{
SqliteTestStatement statement("WITH T(blob) AS (VALUES (?)) SELECT blob FROM T", database);
- Utils::span<const Sqlite::byte> bytes;
+ Sqlite::BlobView bytes;
statement.bind(1, bytes);
statement.next();
@@ -341,7 +334,7 @@ TEST_F(SqliteStatement, BindIndexIsToLargeIsThrowingBindingIndexIsOutOfBoundValu
TEST_F(SqliteStatement, BindIndexIsToLargeIsThrowingBindingIndexIsOutOfBoundBlob)
{
SqliteTestStatement statement("WITH T(blob) AS (VALUES (?)) SELECT blob FROM T", database);
- Utils::span<const Sqlite::byte> bytes;
+ Sqlite::BlobView bytes;
ASSERT_THROW(statement.bind(2, bytes), Sqlite::BindingIndexIsOutOfRange);
}
@@ -408,34 +401,25 @@ TEST_F(SqliteStatement, WriteEmptyBlobs)
{
SqliteTestStatement statement("WITH T(blob) AS (VALUES (?)) SELECT blob FROM T", database);
- Utils::span<const Sqlite::byte> bytes;
+ Sqlite::BlobView bytes;
statement.write(bytes);
ASSERT_THAT(statement.fetchBlobValue(0), IsEmpty());
}
-class Blob
-{
-public:
- Blob(Utils::span<const Sqlite::byte> bytes)
- : bytes(bytes.begin(), bytes.end())
- {}
-
- std::vector<Sqlite::byte> bytes;
-};
-
TEST_F(SqliteStatement, WriteBlobs)
{
SqliteTestStatement statement("INSERT INTO test VALUES ('blob', 40, ?)", database);
SqliteTestStatement readStatement("SELECT value FROM test WHERE name = 'blob'", database);
const unsigned char chars[] = "aaafdfdlll";
auto bytePointer = reinterpret_cast<const Sqlite::byte *>(chars);
- Utils::span<const Sqlite::byte> bytes{bytePointer, sizeof(chars) - 1};
+ Sqlite::BlobView bytes{bytePointer, sizeof(chars) - 1};
statement.write(bytes);
- ASSERT_THAT(readStatement.template value<Blob>(), Optional(Field(&Blob::bytes, Eq(bytes))));
+ ASSERT_THAT(readStatement.template value<Sqlite::Blob>(),
+ Optional(Field(&Sqlite::Blob::bytes, Eq(bytes))));
}
TEST_F(SqliteStatement, CannotWriteToClosedDatabase)
@@ -624,38 +608,38 @@ TEST_F(SqliteStatement, GetBlobValues)
ReadStatement statement("SELECT value FROM test WHERE name='blob'", database);
const int value = 0xDDCCBBAA;
auto bytePointer = reinterpret_cast<const Sqlite::byte *>(&value);
- Utils::span<const Sqlite::byte> bytes{bytePointer, 4};
+ Sqlite::BlobView bytes{bytePointer, 4};
- auto values = statement.values<Blob>(1);
+ auto values = statement.values<Sqlite::Blob>(1);
- ASSERT_THAT(values, ElementsAre(Field(&Blob::bytes, Eq(bytes))));
+ ASSERT_THAT(values, ElementsAre(Field(&Sqlite::Blob::bytes, Eq(bytes))));
}
TEST_F(SqliteStatement, GetEmptyBlobValueForInteger)
{
ReadStatement statement("SELECT value FROM test WHERE name='poo'", database);
- auto value = statement.value<Blob>();
+ auto value = statement.value<Sqlite::Blob>();
- ASSERT_THAT(value, Optional(Field(&Blob::bytes, IsEmpty())));
+ ASSERT_THAT(value, Optional(Field(&Sqlite::Blob::bytes, IsEmpty())));
}
TEST_F(SqliteStatement, GetEmptyBlobValueForFloat)
{
ReadStatement statement("SELECT number FROM test WHERE name='foo'", database);
- auto value = statement.value<Blob>();
+ auto value = statement.value<Sqlite::Blob>();
- ASSERT_THAT(value, Optional(Field(&Blob::bytes, IsEmpty())));
+ ASSERT_THAT(value, Optional(Field(&Sqlite::Blob::bytes, IsEmpty())));
}
TEST_F(SqliteStatement, GetEmptyBlobValueForText)
{
ReadStatement statement("SELECT number FROM test WHERE name='bar'", database);
- auto value = statement.value<Blob>();
+ auto value = statement.value<Sqlite::Blob>();
- ASSERT_THAT(value, Optional(Field(&Blob::bytes, IsEmpty())));
+ ASSERT_THAT(value, Optional(Field(&Sqlite::Blob::bytes, IsEmpty())));
}
TEST_F(SqliteStatement, GetOptionalSingleValueAndMultipleQueryValue)
diff --git a/tests/unit/unittest/sqlitestatementmock.h b/tests/unit/unittest/sqlitestatementmock.h
new file mode 100644
index 0000000000..ad8a73e0a5
--- /dev/null
+++ b/tests/unit/unittest/sqlitestatementmock.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <googletest.h>
+
+#include <sqlitebasestatement.h>
+
+class BaseSqliteStatementMock
+{
+public:
+ MOCK_METHOD(bool, next, ());
+ MOCK_METHOD(void, step, ());
+ MOCK_METHOD(void, reset, ());
+
+ MOCK_METHOD(int, fetchIntValue, (int), (const));
+ MOCK_METHOD(long, fetchLongValue, (int), (const));
+ MOCK_METHOD(long long, fetchLongLongValue, (int), (const));
+ MOCK_METHOD(double, fetchDoubleValue, (int), (const));
+ MOCK_METHOD(Utils::SmallString, fetchSmallStringValue, (int), (const));
+ MOCK_METHOD(Utils::PathString, fetchPathStringValue, (int), (const));
+
+ template<typename Type>
+ Type fetchValue(int column) const;
+
+ MOCK_METHOD(void, bind, (int, int), ());
+ MOCK_METHOD(void, bind, (int, long long), ());
+ MOCK_METHOD(void, bind, (int, double), ());
+ MOCK_METHOD(void, bind, (int, Utils::SmallStringView), ());
+ MOCK_METHOD(void, bind, (int, long) );
+ MOCK_METHOD(int, bindingIndexForName, (Utils::SmallStringView name), (const));
+
+ MOCK_METHOD(void, prepare, (Utils::SmallStringView sqlStatement));
+};
+
+template<>
+int BaseSqliteStatementMock::fetchValue<int>(int column) const
+{
+ return fetchIntValue(column);
+}
+
+template<>
+long BaseSqliteStatementMock::fetchValue<long>(int column) const
+{
+ return fetchLongValue(column);
+}
+
+template<>
+long long BaseSqliteStatementMock::fetchValue<long long>(int column) const
+{
+ return fetchLongLongValue(column);
+}
+
+template<>
+double BaseSqliteStatementMock::fetchValue<double>(int column) const
+{
+ return fetchDoubleValue(column);
+}
+
+template<>
+Utils::SmallString BaseSqliteStatementMock::fetchValue<Utils::SmallString>(int column) const
+{
+ return fetchSmallStringValue(column);
+}
+
+template<>
+Utils::PathString BaseSqliteStatementMock::fetchValue<Utils::PathString>(int column) const
+{
+ return fetchPathStringValue(column);
+}
+
+class SqliteStatementMock : public Sqlite::StatementImplementation<NiceMock<BaseSqliteStatementMock>>
+{
+public:
+ explicit SqliteStatementMock()
+ : Sqlite::StatementImplementation<NiceMock<BaseSqliteStatementMock>>()
+ {}
+
+
+protected:
+ void checkIsWritableStatement();
+};
diff --git a/tests/unit/unittest/sqlitetransactionbackendmock.h b/tests/unit/unittest/sqlitetransactionbackendmock.h
new file mode 100644
index 0000000000..7aad4f1f6a
--- /dev/null
+++ b/tests/unit/unittest/sqlitetransactionbackendmock.h
@@ -0,0 +1,46 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+
+#include "googletest.h"
+
+#include <sqlitetransaction.h>
+
+class SqliteTransactionBackendMock : public Sqlite::TransactionInterface
+{
+public:
+ MOCK_METHOD(void, deferredBegin, (), (override));
+ MOCK_METHOD(void, immediateBegin, (), (override));
+ MOCK_METHOD(void, exclusiveBegin, (), (override));
+ MOCK_METHOD(void, commit, (), (override));
+ MOCK_METHOD(void, rollback, (), (override));
+ MOCK_METHOD(void, lock, (), (override));
+ MOCK_METHOD(void, unlock, (), (override));
+ MOCK_METHOD(void, immediateSessionBegin, (), (override));
+ MOCK_METHOD(void, sessionCommit, (), (override));
+ MOCK_METHOD(void, sessionRollback, (), (override));
+};
diff --git a/tests/unit/unittest/sqlitewritestatementmock.cpp b/tests/unit/unittest/sqlitewritestatementmock.cpp
new file mode 100644
index 0000000000..a3612833b2
--- /dev/null
+++ b/tests/unit/unittest/sqlitewritestatementmock.cpp
@@ -0,0 +1,35 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#include "sqlitewritestatementmock.h"
+
+#include "sqlitedatabasemock.h"
+
+SqliteWriteStatementMock::SqliteWriteStatementMock(Utils::SmallStringView sqlStatement,
+ SqliteDatabaseMock &database)
+ : sqlStatement(sqlStatement)
+{
+ database.prepare(sqlStatement);
+}
diff --git a/tests/unit/unittest/sqlitewritestatementmock.h b/tests/unit/unittest/sqlitewritestatementmock.h
new file mode 100644
index 0000000000..3a8d159329
--- /dev/null
+++ b/tests/unit/unittest/sqlitewritestatementmock.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "googletest.h"
+
+#include <sqliteblob.h>
+#include <sqlitevalue.h>
+
+class SqliteDatabaseMock;
+
+class SqliteWriteStatementMock
+{
+public:
+ SqliteWriteStatementMock() = default;
+ SqliteWriteStatementMock(Utils::SmallStringView sqlStatement, SqliteDatabaseMock &database);
+
+ MOCK_METHOD(void, execute, (), ());
+
+ MOCK_METHOD(void, write, (Utils::SmallStringView), ());
+ MOCK_METHOD(void, write, (long long), ());
+ MOCK_METHOD(void, write, (Utils::SmallStringView, Utils::SmallStringView), ());
+ MOCK_METHOD(void, write, (long long, Utils::SmallStringView), ());
+ MOCK_METHOD(void,
+ write,
+ (Utils::SmallStringView, Utils::SmallStringView, Utils::SmallStringView),
+ ());
+ MOCK_METHOD(void, write, (Utils::SmallStringView, Utils::SmallStringView, long long), ());
+ MOCK_METHOD(void, write, (Utils::SmallStringView, Utils::SmallStringView, double), ());
+ MOCK_METHOD(void, write, (long long, Utils::SmallStringView, Utils::SmallStringView), ());
+ MOCK_METHOD(void, write, (long long, Utils::SmallStringView, const Sqlite::Value &), ());
+ MOCK_METHOD(void, write, (Utils::SmallStringView, long long, Sqlite::BlobView, Sqlite::BlobView), ());
+ MOCK_METHOD(void,
+ write,
+ (Utils::SmallStringView, long long, Sqlite::NullValue, Sqlite::NullValue),
+ ());
+ MOCK_METHOD(void,
+ write,
+ (Utils::SmallStringView,
+ Utils::SmallStringView,
+ Utils::SmallStringView,
+ Utils::SmallStringView),
+ ());
+ MOCK_METHOD(void,
+ write,
+ (long long, Utils::SmallStringView, Utils::SmallStringView, Utils::SmallStringView),
+ ());
+ MOCK_METHOD(void, write, (long long, long long, Utils::SmallStringView, Utils::SmallStringView), ());
+ MOCK_METHOD(void,
+ write,
+ (Utils::SmallStringView,
+ Utils::SmallStringView,
+ Utils::SmallStringView,
+ Utils::SmallStringView,
+ Utils::SmallStringView),
+ ());
+
+ MOCK_METHOD(void, write, (void *, long long), ());
+
+ Utils::SmallString sqlStatement;
+};
diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro
index 967463bbdc..6200a24d7f 100644
--- a/tests/unit/unittest/unittest.pro
+++ b/tests/unit/unittest/unittest.pro
@@ -66,6 +66,9 @@ SOURCES += \
filepathview-test.cpp \
gtest-creator-printing.cpp \
gtest-qt-printing.cpp \
+ imagecache-test.cpp \
+ imagecachegenerator-test.cpp \
+ imagecachestorage-test.cpp \
lastchangedrowid-test.cpp \
lineprefixer-test.cpp \
listmodeleditor-test.cpp \
@@ -134,7 +137,9 @@ SOURCES += \
sqlitestatement-test.cpp \
sqlitetable-test.cpp \
sqlstatementbuilder-test.cpp \
- createtablesqlstatementbuilder-test.cpp
+ createtablesqlstatementbuilder-test.cpp \
+ sqlitereadstatementmock.cpp \
+ sqlitewritestatementmock.cpp
!isEmpty(QTC_UNITTEST_BUILD_CPP_PARSER):SOURCES += matchingtext-test.cpp
@@ -240,12 +245,15 @@ HEADERS += \
gtest-llvm-printing.h \
gtest-qt-printing.h \
gtest-std-printing.h \
+ imagecachecollectormock.h \
mimedatabase-utilities.h \
mockclangcodemodelclient.h \
mockclangcodemodelserver.h \
mockclangpathwatcher.h \
mockclangpathwatchernotifier.h \
mockfilesystem.h \
+ mockimagecachegenerator.h \
+ mockimagecachestorage.h \
mocklistmodeleditorview.h \
mockpchcreator.h \
mockpchmanagerclient.h \
@@ -258,6 +266,8 @@ HEADERS += \
mocksearchhandle.h \
mocksearchresult.h \
mocksyntaxhighligher.h \
+ mocktimestampprovider.h \
+ notification.h \
processevents-utilities.h \
sourcerangecontainer-matcher.h \
spydummy.h \
@@ -301,7 +311,12 @@ HEADERS += \
mockpchtaskgenerator.h \
../mockup/qmldesigner/designercore/include/nodeinstanceview.h \
../mockup/qmldesigner/designercore/include/rewriterview.h \
- ../mockup/qmldesigner/designercore/include/itemlibraryitem.h
+ ../mockup/qmldesigner/designercore/include/itemlibraryitem.h\
+ sqlitedatabasemock.h \
+ sqlitereadstatementmock.h \
+ sqlitestatementmock.h \
+ sqlitetransactionbackendmock.h \
+ sqlitewritestatementmock.h
!isEmpty(LIBCLANG_LIBS) {
diff --git a/tests/unit/unittest/unittest.qbs b/tests/unit/unittest/unittest.qbs
index 5ceb723b22..ac7eb08b82 100644
--- a/tests/unit/unittest/unittest.qbs
+++ b/tests/unit/unittest/unittest.qbs
@@ -333,6 +333,19 @@ Project {
"unittests-main.cpp",
"usedmacrofilter-test.cpp",
"utf8-test.cpp",
+ "imagecache-test.cpp",
+ "imagecachegenerator-test.cpp",
+ "imagecachestorage-test.cpp",
+ "sqlitedatabasemock.h",
+ "sqlitereadstatementmock.h",
+ "sqlitestatementmock.h",
+ "sqlitetransactionbackendmock.h",
+ "sqlitewritestatementmock.h",
+ "notification.h",
+ "mocktimestampprovider.h",
+ "imagecachecollectormock.h",
+ "mockimagecachegenerator.h",
+ "mockimagecachestorage.h",
]
Group {