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>
This commit is contained in:
Marco Bubke
2020-09-16 13:44:43 +02:00
committed by Tim Jenssen
parent 58e612c85f
commit d1b0c12d6b
111 changed files with 4407 additions and 241 deletions

View File

@@ -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;
};

View File

@@ -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 &) {}

View File

@@ -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 \

View File

@@ -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")

View File

@@ -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

View File

@@ -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

View File

@@ -100,7 +100,7 @@ void Qt5CapturePreviewNodeInstanceServer::collectItemChangesAndSendChangeCommand
stateInstance.deactivateState();
}
nodeInstanceClient()->capturedData(CapturedDataCommand{stateDatas});
nodeInstanceClient()->capturedData(CapturedDataCommand{std::move(stateDatas)});
slowDownRenderTimer();
inFunction = false;

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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[])
{

View File

@@ -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)
}

View File

@@ -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()
}
}
}

View File

@@ -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

View File

@@ -44,4 +44,6 @@ add_qtc_library(Sqlite
tableconstraints.h
utf8string.cpp utf8string.h
utf8stringvector.cpp utf8stringvector.h
sqliteblob.h
sqlitetimestamp.h
)

View File

@@ -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 \

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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)};
}

View File

@@ -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
{

View File

@@ -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();

View File

@@ -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

View File

@@ -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 };

View File

@@ -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;

View File

@@ -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

View File

@@ -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 \

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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;

View File

@@ -29,6 +29,8 @@
#include "itemlibraryitem.h"
#include "itemlibrarysection.h"
#include <components/previewtooltip/previewtooltipbackend.h>
#include <model.h>
#include <nodehints.h>
#include <nodemetainfo.h>

View File

@@ -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

View File

@@ -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;

View File

@@ -27,19 +27,21 @@
#include "customfilesystemmodel.h"
#include "itemlibraryassetimportdialog.h"
#include "itemlibraryiconimageprovider.h"
#include <theme.h>
#include <itemlibrarymodel.h>
#include <itemlibraryimageprovider.h>
#include <itemlibraryinfo.h>
#include <metainfo.h>
#include <model.h>
#include <rewritingexception.h>
#include <qmldesignerplugin.h>
#include <qmldesignerconstants.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 <qmldesignerconstants.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>{
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));
{{"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

View File

@@ -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;

View File

@@ -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>

View File

@@ -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}));
}
}

View File

@@ -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;
};
}

View File

@@ -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>

View File

@@ -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

View File

@@ -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)

View File

@@ -0,0 +1,11 @@
HEADERS += \
$$PWD/previewtooltipbackend.h \
$$PWD/previewimagetooltip.h
SOURCES += \
$$PWD/previewtooltipbackend.cpp \
$$PWD/previewimagetooltip.cpp
FORMS += $$PWD/previewimagetooltip.ui

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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,
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;
};

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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()

View File

@@ -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;
};

View File

@@ -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:

View File

@@ -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)

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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(

View File

@@ -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);

View File

@@ -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;

View File

@@ -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(),

View File

@@ -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()

View File

@@ -3,7 +3,8 @@ QTC_LIB_DEPENDS += \
utils \
qmljs \
qmleditorwidgets \
advanceddockingsystem
advanceddockingsystem \
sqlite
QTC_PLUGIN_DEPENDS += \
coreplugin \
texteditor \

View File

@@ -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) {

View File

@@ -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",

View File

@@ -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 \

View File

@@ -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

View File

@@ -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",

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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)

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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

View File

@@ -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));
};

View File

@@ -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

View File

@@ -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

View File

@@ -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));
};

View File

@@ -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));
};

View File

@@ -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));
};

Some files were not shown because too many files have changed in this diff Show More