QmlDesigner: Improve imagecache

Instead of coding some arguments to extraId(state) we provide now a
std::variant there extra arguments can be saved. Because it's a
std::variant it can be easlily extended by new structs. There is a new
synchronous interface too. It has an extra method for QIcon which saves
icons in an extra table. It would be even nicer if we would have a
mipmap image too. So we could do it asynchonously too but so far it works
only in the main thread.

Task-number: QDS-3579
Fixes: QDS-3584
Change-Id: If368d84d82308a91a5f4f037021e749c9ef868ed
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Marco Bubke
2021-01-13 13:23:46 +01:00
parent 3ffc7271e5
commit 7dc72c533e
55 changed files with 2063 additions and 904 deletions

View File

@@ -509,8 +509,10 @@ extend_qtc_plugin(QmlDesigner
include/textmodifier.h
include/variantproperty.h
include/viewmanager.h
include/imagecache.h
include/imagecacheinterface.h
include/asynchronousimagecache.h
include/synchronousimagecache.h
include/imagecacheauxiliarydata.h
include/asynchronousimagecacheinterface.h
)
extend_qtc_plugin(QmlDesigner
@@ -601,7 +603,8 @@ extend_qtc_plugin(QmlDesigner
imagecache/imagecachecollector.cpp
imagecache/imagecachefontcollector.h
imagecache/imagecachefontcollector.cpp
imagecache/imagecache.cpp
imagecache/asynchronousimagecache.cpp
imagecache/synchronousimagecache.cpp
imagecache/imagecachecollectorinterface.h
imagecache/imagecacheconnectionmanager.cpp
imagecache/imagecacheconnectionmanager.h

View File

@@ -25,8 +25,8 @@
#include "customfilesystemmodel.h"
#include <synchronousimagecache.h>
#include <theme.h>
#include <imagecache.h>
#include <utils/filesystemwatcher.h>
@@ -98,7 +98,7 @@ QString fontFamily(const QFileInfo &info)
class ItemLibraryFileIconProvider : public QFileIconProvider
{
public:
ItemLibraryFileIconProvider(ImageCache &fontImageCache)
ItemLibraryFileIconProvider(SynchronousImageCache &fontImageCache)
: QFileIconProvider()
, m_fontImageCache(fontImageCache)
{
@@ -138,71 +138,29 @@ public:
QIcon generateFontIcons(const QString &filePath) const
{
QIcon icon;
QString colorName = Theme::getColor(Theme::DStextColor).name();
std::condition_variable condition;
int count = iconSizes.size();
std::mutex mutex;
QList<QPair<QSize, QImage>> images;
for (auto iconSize : iconSizes) {
m_fontImageCache.requestImage(
filePath,
[&images, &condition, &count, &mutex, iconSize](const QImage &image) {
int currentCount;
{
std::unique_lock lock{mutex};
currentCount = --count;
images.append({iconSize, image});
}
if (currentCount <= 0)
condition.notify_all();
},
[&images, &condition, &count, &mutex, iconSize] {
int currentCount;
{
std::unique_lock lock{mutex};
currentCount = --count;
images.append({iconSize, {}});
}
if (currentCount <= 0)
condition.notify_all();
},
QStringLiteral("%1@%2@Abc").arg(QString::number(iconSize.width()),
colorName)
);
}
{
// Block main thread until icons are generated, as it has to be done synchronously
std::unique_lock lock{mutex};
if (count > 0)
condition.wait(lock, [&]{ return count <= 0; });
}
for (const auto &pair : qAsConst(images)) {
QImage image = pair.second;
if (image.isNull())
icon.addPixmap(defaultPixmapForType("font", pair.first));
else
icon.addPixmap(QPixmap::fromImage(image));
}
return icon;
return m_fontImageCache.icon(
filePath,
{},
ImageCache::FontCollectorSizesAuxiliaryData{Utils::span{iconSizes},
Theme::getColor(Theme::DStextColor).name(),
"Abc"});
}
// Generated icon sizes should contain all ItemLibraryResourceView needed icon sizes, and their
// x2 versions for HDPI sceens
QList<QSize> iconSizes = {{384, 384}, {192, 192}, // Large
{256, 256}, {128, 128}, // Drag
{96, 96}, // Medium
{48, 48}, // Small
{64, 64}, {32, 32}}; // List
std::vector<QSize> iconSizes = {{384, 384},
{192, 192}, // Large
{256, 256},
{128, 128}, // Drag
{96, 96}, // Medium
{48, 48}, // Small
{64, 64},
{32, 32}}; // List
ImageCache &m_fontImageCache;
SynchronousImageCache &m_fontImageCache;
};
CustomFileSystemModel::CustomFileSystemModel(ImageCache &fontImageCache, QObject *parent)
CustomFileSystemModel::CustomFileSystemModel(SynchronousImageCache &fontImageCache, QObject *parent)
: QAbstractListModel(parent)
, m_fileSystemModel(new QFileSystemModel(this))
, m_fileSystemWatcher(new Utils::FileSystemWatcher(this))

View File

@@ -39,13 +39,14 @@ namespace Utils { class FileSystemWatcher; }
namespace QmlDesigner {
class ImageCache;
class SynchronousImageCache;
class CustomFileSystemModel : public QAbstractListModel
{
Q_OBJECT
public:
CustomFileSystemModel(ImageCache &fontImageCache, QObject *parent = nullptr);
CustomFileSystemModel(QmlDesigner::SynchronousImageCache &fontImageCache,
QObject *parent = nullptr);
void setFilter(QDir::Filters filters);
QString rootPath() const;

View File

@@ -66,7 +66,7 @@ QQuickImageResponse *ItemLibraryIconImageProvider::requestImageResponse(const QS
{
auto response = std::make_unique<ImageRespose>();
m_cache.requestIcon(
m_cache.requestSmallImage(
id,
[response = QPointer<ImageRespose>(response.get())](const QImage &image) {
QMetaObject::invokeMethod(

View File

@@ -29,7 +29,7 @@
#include <rewriterview.h>
#include <coreplugin/icore.h>
#include <imagecache.h>
#include <asynchronousimagecache.h>
#include <imagecache/imagecachecollector.h>
#include <imagecache/imagecacheconnectionmanager.h>
#include <imagecache/imagecachegenerator.h>
@@ -45,14 +45,14 @@ namespace QmlDesigner {
class ItemLibraryIconImageProvider : public QQuickAsyncImageProvider
{
public:
ItemLibraryIconImageProvider(ImageCache &imageCache)
ItemLibraryIconImageProvider(AsynchronousImageCache &imageCache)
: m_cache{imageCache}
{}
QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override;
private:
ImageCache &m_cache;
AsynchronousImageCache &m_cache;
};
} // namespace QmlDesigner

View File

@@ -28,7 +28,7 @@
#include "customfilesystemmodel.h"
#include <theme.h>
#include <imagecache.h>
#include <asynchronousimagecache.h>
#include <QAction>
#include <QActionGroup>
@@ -62,8 +62,9 @@ void ItemLibraryResourceView::addSizeAction(QActionGroup *group, const QString &
});
}
ItemLibraryResourceView::ItemLibraryResourceView(ImageCache &fontImageCache, QWidget *parent) :
QListView(parent)
ItemLibraryResourceView::ItemLibraryResourceView(AsynchronousImageCache &fontImageCache,
QWidget *parent)
: QListView(parent)
{
setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
@@ -113,13 +114,12 @@ ItemLibraryResourceView::ItemLibraryResourceView(ImageCache &fontImageCache, QWi
// a commonly used sentence to preview the font glyphs in latin fonts.
// For fonts that do not have latin glyphs, the font family name will have to
// suffice for preview. Font family name is inserted into %1 at render time.
m_fontPreviewTooltipBackend->setState(QStringLiteral("%1@%2@%3")
.arg(QString::number(300),
Theme::getColor(Theme::DStextColor).name(),
QStringLiteral("%1\n\n"
"The quick brown fox jumps\n"
"over the lazy dog\n"
"1234567890")));
m_fontPreviewTooltipBackend->setAuxiliaryData(
ImageCache::FontCollectorSizeAuxiliaryData{QSize{300, 300},
Theme::getColor(Theme::DStextColor).name(),
QStringLiteral("The quick brown fox jumps\n"
"over the lazy dog\n"
"1234567890")});
}
void ItemLibraryResourceView::startDrag(Qt::DropActions /* supportedActions */)

View File

@@ -35,13 +35,14 @@ QT_END_NAMESPACE
namespace QmlDesigner {
class ImageCache;
class AsynchronousImageCache;
class ItemLibraryResourceView : public QListView {
Q_OBJECT
public:
explicit ItemLibraryResourceView(ImageCache &fontImageCache, QWidget *parent = nullptr);
explicit ItemLibraryResourceView(AsynchronousImageCache &fontImageCache,
QWidget *parent = nullptr);
void startDrag(Qt::DropActions supportedActions) override;
bool viewportEvent(QEvent *event) override;

View File

@@ -26,12 +26,12 @@
#include "itemlibraryview.h"
#include "itemlibrarywidget.h"
#include "metainfo.h"
#include <asynchronousimagecache.h>
#include <bindingproperty.h>
#include <coreplugin/icore.h>
#include <imagecache.h>
#include <imagecache/imagecachecollector.h>
#include <imagecache/imagecachefontcollector.h>
#include <imagecache/imagecacheconnectionmanager.h>
#include <imagecache/imagecachefontcollector.h>
#include <imagecache/imagecachegenerator.h>
#include <imagecache/imagecachestorage.h>
#include <imagecache/timestampprovider.h>
@@ -42,6 +42,7 @@
#include <projectexplorer/target.h>
#include <rewriterview.h>
#include <sqlitedatabase.h>
#include <synchronousimagecache.h>
#include <utils/algorithm.h>
#include <qmldesignerplugin.h>
#include <qmlitemnode.h>
@@ -52,7 +53,7 @@ class ImageCacheData
{
public:
Sqlite::Database database{
Utils::PathString{Core::ICore::cacheResourcePath() + "/imagecache-v1.db"}};
Utils::PathString{Core::ICore::cacheResourcePath() + "/imagecache-v2.db"}};
ImageCacheStorage<Sqlite::Database> storage{database};
ImageCacheConnectionManager connectionManager;
ImageCacheCollector collector{connectionManager};
@@ -60,8 +61,9 @@ public:
ImageCacheGenerator generator{collector, storage};
ImageCacheGenerator fontGenerator{fontCollector, storage};
TimeStampProvider timeStampProvider;
ImageCache cache{storage, generator, timeStampProvider};
ImageCache fontImageCache{storage, fontGenerator, timeStampProvider};
AsynchronousImageCache cache{storage, generator, timeStampProvider};
AsynchronousImageCache asynchronousFontImageCache{storage, fontGenerator, timeStampProvider};
SynchronousImageCache synchronousFontImageCache{storage, timeStampProvider, fontCollector};
};
ItemLibraryView::ItemLibraryView(QObject* parent)
@@ -82,7 +84,9 @@ bool ItemLibraryView::hasWidget() const
WidgetInfo ItemLibraryView::widgetInfo()
{
if (m_widget.isNull()) {
m_widget = new ItemLibraryWidget{m_imageCacheData->cache, m_imageCacheData->fontImageCache};
m_widget = new ItemLibraryWidget{m_imageCacheData->cache,
m_imageCacheData->asynchronousFontImageCache,
m_imageCacheData->synchronousFontImageCache};
m_widget->setImportsWidget(m_importManagerView->widgetInfo().widget);
}
@@ -159,12 +163,14 @@ void ItemLibraryView::importsChanged(const QList<Import> &addedImports, const QL
void ItemLibraryView::setResourcePath(const QString &resourcePath)
{
if (m_widget.isNull())
m_widget = new ItemLibraryWidget{m_imageCacheData->cache, m_imageCacheData->fontImageCache};
m_widget = new ItemLibraryWidget{m_imageCacheData->cache,
m_imageCacheData->asynchronousFontImageCache,
m_imageCacheData->synchronousFontImageCache};
m_widget->setResourcePath(resourcePath);
}
ImageCache &ItemLibraryView::imageCache()
AsynchronousImageCache &ItemLibraryView::imageCache()
{
return m_imageCacheData->cache;
}

View File

@@ -35,7 +35,7 @@ namespace QmlDesigner {
class ItemLibraryWidget;
class ImportManagerView;
class ImageCacheData;
class ImageCache;
class AsynchronousImageCache;
class ItemLibraryView : public AbstractView
{
@@ -56,7 +56,7 @@ public:
void setResourcePath(const QString &resourcePath);
ImageCache &imageCache();
AsynchronousImageCache &imageCache();
protected:
void updateImports();

View File

@@ -82,10 +82,12 @@ static QString propertyEditorResourcesPath() {
return Core::ICore::resourcePath() + QStringLiteral("/qmldesigner/propertyEditorQmlSources");
}
ItemLibraryWidget::ItemLibraryWidget(ImageCache &imageCache, ImageCache &fontImageCache)
ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache,
AsynchronousImageCache &asynchronousFontImageCache,
SynchronousImageCache &synchronousFontImageCache)
: m_itemIconSize(24, 24)
, m_itemViewQuickWidget(new QQuickWidget(this))
, m_resourcesView(new ItemLibraryResourceView(fontImageCache, this))
, m_resourcesView(new ItemLibraryResourceView(asynchronousFontImageCache, this))
, m_importTagsWidget(new QWidget(this))
, m_addResourcesWidget(new QWidget(this))
, m_imageCache{imageCache}
@@ -118,7 +120,7 @@ ItemLibraryWidget::ItemLibraryWidget(ImageCache &imageCache, ImageCache &fontIma
Theme::getColor(Theme::Color::QmlDesigner_BackgroundColorDarkAlternate));
/* create Resources view and its model */
m_resourcesFileSystemModel = new CustomFileSystemModel(fontImageCache, this);
m_resourcesFileSystemModel = new CustomFileSystemModel(synchronousFontImageCache, this);
m_resourcesView->setModel(m_resourcesFileSystemModel.data());
/* create image provider for loading item icons */

View File

@@ -56,7 +56,8 @@ class CustomFileSystemModel;
class ItemLibraryModel;
class ItemLibraryResourceView;
class ImageCache;
class SynchronousImageCache;
class AsynchronousImageCache;
class ImageCacheCollector;
class ItemLibraryWidget : public QFrame
@@ -69,7 +70,9 @@ class ItemLibraryWidget : public QFrame
};
public:
ItemLibraryWidget(ImageCache &imageCache, ImageCache &fontImageCache);
ItemLibraryWidget(AsynchronousImageCache &imageCache,
AsynchronousImageCache &asynchronousFontImageCache,
SynchronousImageCache &synchronousFontImageCache);
~ItemLibraryWidget();
void setItemLibraryInfo(ItemLibraryInfo *itemLibraryInfo);
@@ -126,7 +129,7 @@ private:
std::unique_ptr<PreviewTooltipBackend> m_previewTooltipBackend;
QShortcut *m_qmlSourceUpdateShortcut;
ImageCache &m_imageCache;
AsynchronousImageCache &m_imageCache;
QPointer<Model> m_model;
FilterChangeFlag m_filterFlag;
ItemLibraryEntry m_currentitemLibraryEntry;

View File

@@ -28,7 +28,7 @@
#include "previewimagetooltip.h"
#include <coreplugin/icore.h>
#include <imagecache.h>
#include <asynchronousimagecache.h>
#include <QApplication>
#include <QMetaObject>
@@ -36,7 +36,7 @@
namespace QmlDesigner {
PreviewTooltipBackend::PreviewTooltipBackend(ImageCache &cache)
PreviewTooltipBackend::PreviewTooltipBackend(AsynchronousImageCache &cache)
: m_cache{cache}
{}
@@ -64,8 +64,8 @@ void PreviewTooltipBackend::showTooltip()
});
},
[] {},
m_state
);
m_extraId,
m_auxiliaryData);
reposition();
}
@@ -155,17 +155,17 @@ void PreviewTooltipBackend::setInfo(const QString &info)
emit infoChanged();
}
QString PreviewTooltipBackend::state() const
QString PreviewTooltipBackend::extraId() const
{
return m_state;
return m_extraId;
}
// Sets the imageCache state hint. Valid content depends on image cache collector used.
void PreviewTooltipBackend::setState(const QString &state)
// Sets the imageCache extraId hint. Valid content depends on image cache collector used.
void PreviewTooltipBackend::setExtraId(const QString &extraId)
{
m_state = state;
if (m_state != state)
emit stateChanged();
m_extraId = extraId;
if (m_extraId != extraId)
emit extraIdChanged();
}
} // namespace QmlDesigner

View File

@@ -25,6 +25,8 @@
#pragma once
#include <imagecacheauxiliarydata.h>
#include <QObject>
#include <QQmlEngine>
@@ -33,7 +35,7 @@
namespace QmlDesigner {
class PreviewImageTooltip;
class ImageCache;
class AsynchronousImageCache;
class PreviewTooltipBackend : public QObject
{
@@ -42,10 +44,10 @@ class PreviewTooltipBackend : public QObject
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
Q_PROPERTY(QString info READ info WRITE setInfo NOTIFY infoChanged)
Q_PROPERTY(QString state READ state WRITE setState NOTIFY stateChanged)
Q_PROPERTY(QString extraId READ extraId WRITE setExtraId NOTIFY extraIdChanged)
public:
PreviewTooltipBackend(ImageCache &cache);
PreviewTooltipBackend(AsynchronousImageCache &cache);
~PreviewTooltipBackend();
Q_INVOKABLE void showTooltip();
@@ -58,24 +60,30 @@ public:
void setPath(const QString &path);
QString info() const;
void setInfo(const QString &info);
QString state() const;
void setState(const QString &state);
QString extraId() const;
void setExtraId(const QString &extraId);
bool isVisible() const;
void setAuxiliaryData(ImageCache::AuxiliaryData auxiliaryData)
{
m_auxiliaryData = std::move(auxiliaryData);
}
signals:
void nameChanged();
void pathChanged();
void infoChanged();
void stateChanged();
void extraIdChanged();
private:
QString m_name;
QString m_path;
QString m_info;
QString m_state;
QString m_extraId;
std::unique_ptr<PreviewImageTooltip> m_tooltip;
ImageCache &m_cache;
ImageCache::AuxiliaryData m_auxiliaryData;
AsynchronousImageCache &m_cache;
};
}

View File

@@ -88,7 +88,8 @@ SOURCES += $$PWD/model/abstractview.cpp \
$$PWD/model/qmltimelinekeyframegroup.cpp \
$$PWD/model/annotation.cpp \
$$PWD/model/stylesheetmerger.cpp \
$$PWD/imagecache/imagecache.cpp \
$$PWD/imagecache/asynchronousimagecache.cpp \
$$PWD/imagecache/synchronousimagecache.cpp \
$$PWD/imagecache/imagecacheconnectionmanager.cpp \
$$PWD/imagecache/imagecachegenerator.cpp \
$$PWD/imagecache/timestampprovider.cpp
@@ -174,7 +175,9 @@ HEADERS += $$PWD/include/qmldesignercorelib_global.h \
$$PWD/include/qmltimelinekeyframegroup.h \
$$PWD/include/annotation.h \
$$PWD/include/stylesheetmerger.h \
$$PWD/include/imagecache.h \
$$PWD/include/asynchronousimagecache.h \
$$PWD/include/synchronousimagecache.h \
$$PWD/include/imagecacheauxiliarydata.h \
$$PWD/imagecache/imagecachecollectorinterface.h \
$$PWD/imagecache/imagecacheconnectionmanager.h \
$$PWD/imagecache/imagecachegeneratorinterface.h \

View File

@@ -23,7 +23,7 @@
**
****************************************************************************/
#include "imagecache.h"
#include "asynchronousimagecache.h"
#include "imagecachegenerator.h"
#include "imagecachestorage.h"
@@ -33,9 +33,9 @@
namespace QmlDesigner {
ImageCache::ImageCache(ImageCacheStorageInterface &storage,
ImageCacheGeneratorInterface &generator,
TimeStampProviderInterface &timeStampProvider)
AsynchronousImageCache::AsynchronousImageCache(ImageCacheStorageInterface &storage,
ImageCacheGeneratorInterface &generator,
TimeStampProviderInterface &timeStampProvider)
: m_storage(storage)
, m_generator(generator)
, m_timeStampProvider(timeStampProvider)
@@ -44,10 +44,11 @@ ImageCache::ImageCache(ImageCacheStorageInterface &storage,
while (isRunning()) {
if (auto [hasEntry, entry] = getEntry(); hasEntry) {
request(entry.name,
entry.state,
entry.extraId,
entry.requestType,
std::move(entry.captureCallback),
std::move(entry.abortCallback),
std::move(entry.auxiliaryData),
m_storage,
m_generator,
m_timeStampProvider);
@@ -58,26 +59,27 @@ ImageCache::ImageCache(ImageCacheStorageInterface &storage,
}};
}
ImageCache::~ImageCache()
AsynchronousImageCache::~AsynchronousImageCache()
{
clean();
wait();
}
void ImageCache::request(Utils::SmallStringView name,
Utils::SmallStringView state,
ImageCache::RequestType requestType,
ImageCache::CaptureCallback captureCallback,
ImageCache::AbortCallback abortCallback,
ImageCacheStorageInterface &storage,
ImageCacheGeneratorInterface &generator,
TimeStampProviderInterface &timeStampProvider)
void AsynchronousImageCache::request(Utils::SmallStringView name,
Utils::SmallStringView extraId,
AsynchronousImageCache::RequestType requestType,
AsynchronousImageCache::CaptureImageCallback captureCallback,
AsynchronousImageCache::AbortCallback abortCallback,
ImageCache::AuxiliaryData auxiliaryData,
ImageCacheStorageInterface &storage,
ImageCacheGeneratorInterface &generator,
TimeStampProviderInterface &timeStampProvider)
{
const auto id = state.empty() ? Utils::PathString{name} : Utils::PathString{name, "+", state};
const auto id = extraId.empty() ? Utils::PathString{name} : Utils::PathString{name, "+", extraId};
const auto timeStamp = timeStampProvider.timeStamp(name);
const auto entry = requestType == RequestType::Image ? storage.fetchImage(id, timeStamp)
: storage.fetchIcon(id, timeStamp);
: storage.fetchSmallImage(id, timeStamp);
if (entry.hasEntry) {
if (entry.image.isNull())
@@ -85,15 +87,20 @@ void ImageCache::request(Utils::SmallStringView name,
else
captureCallback(entry.image);
} else {
auto callback = [captureCallback = std::move(captureCallback),
requestType](const QImage &image, const QImage &smallImage) {
captureCallback(requestType == RequestType::Image ? image : smallImage);
};
generator.generateImage(name,
state,
extraId,
timeStamp,
std::move(captureCallback),
std::move(abortCallback));
std::move(callback),
std::move(abortCallback),
std::move(auxiliaryData));
}
}
void ImageCache::wait()
void AsynchronousImageCache::wait()
{
stopThread();
m_condition.notify_all();
@@ -101,46 +108,50 @@ void ImageCache::wait()
m_backgroundThread.join();
}
void ImageCache::requestImage(Utils::PathString name,
ImageCache::CaptureCallback captureCallback,
AbortCallback abortCallback,
Utils::SmallString state)
void AsynchronousImageCache::requestImage(Utils::PathString name,
AsynchronousImageCache::CaptureImageCallback captureCallback,
AbortCallback abortCallback,
Utils::SmallString extraId,
ImageCache::AuxiliaryData auxiliaryData)
{
addEntry(std::move(name),
std::move(state),
std::move(extraId),
std::move(captureCallback),
std::move(abortCallback),
std::move(auxiliaryData),
RequestType::Image);
m_condition.notify_all();
}
void ImageCache::requestIcon(Utils::PathString name,
ImageCache::CaptureCallback captureCallback,
ImageCache::AbortCallback abortCallback,
Utils::SmallString state)
void AsynchronousImageCache::requestSmallImage(Utils::PathString name,
AsynchronousImageCache::CaptureImageCallback captureCallback,
AsynchronousImageCache::AbortCallback abortCallback,
Utils::SmallString extraId,
ImageCache::AuxiliaryData auxiliaryData)
{
addEntry(std::move(name),
std::move(state),
std::move(extraId),
std::move(captureCallback),
std::move(abortCallback),
RequestType::Icon);
std::move(auxiliaryData),
RequestType::SmallImage);
m_condition.notify_all();
}
void ImageCache::clean()
void AsynchronousImageCache::clean()
{
clearEntries();
m_generator.clean();
}
void ImageCache::waitForFinished()
void AsynchronousImageCache::waitForFinished()
{
wait();
m_generator.waitForFinished();
}
std::tuple<bool, ImageCache::Entry> ImageCache::getEntry()
std::tuple<bool, AsynchronousImageCache::Entry> AsynchronousImageCache::getEntry()
{
std::unique_lock lock{m_mutex};
@@ -153,22 +164,24 @@ std::tuple<bool, ImageCache::Entry> ImageCache::getEntry()
return {true, entry};
}
void ImageCache::addEntry(Utils::PathString &&name,
Utils::SmallString &&state,
ImageCache::CaptureCallback &&captureCallback,
AbortCallback &&abortCallback,
RequestType requestType)
void AsynchronousImageCache::addEntry(Utils::PathString &&name,
Utils::SmallString &&extraId,
AsynchronousImageCache::CaptureImageCallback &&captureCallback,
AbortCallback &&abortCallback,
ImageCache::AuxiliaryData &&auxiliaryData,
RequestType requestType)
{
std::unique_lock lock{m_mutex};
m_entries.emplace_back(std::move(name),
std::move(state),
std::move(extraId),
std::move(captureCallback),
std::move(abortCallback),
std::move(auxiliaryData),
requestType);
}
void ImageCache::clearEntries()
void AsynchronousImageCache::clearEntries()
{
std::unique_lock lock{m_mutex};
for (Entry &entry : m_entries)
@@ -176,20 +189,20 @@ void ImageCache::clearEntries()
m_entries.clear();
}
void ImageCache::waitForEntries()
void AsynchronousImageCache::waitForEntries()
{
std::unique_lock lock{m_mutex};
if (m_entries.empty())
m_condition.wait(lock, [&] { return m_entries.size() || m_finishing; });
}
void ImageCache::stopThread()
void AsynchronousImageCache::stopThread()
{
std::unique_lock lock{m_mutex};
m_finishing = true;
}
bool ImageCache::isRunning()
bool AsynchronousImageCache::isRunning()
{
std::unique_lock lock{m_mutex};
return !m_finishing || m_entries.size();

View File

@@ -68,6 +68,7 @@ ImageCacheCollector::~ImageCacheCollector() = default;
void ImageCacheCollector::start(Utils::SmallStringView name,
Utils::SmallStringView state,
const ImageCache::AuxiliaryData &auxiliaryData,
CaptureCallback captureCallback,
AbortCallback abortCallback)
{
@@ -97,7 +98,15 @@ void ImageCacheCollector::start(Utils::SmallStringView name,
if (stateNode.isValid())
rewriterView.setCurrentStateNode(stateNode);
m_connectionManager.setCallback(std::move(captureCallback));
auto callback = [captureCallback = std::move(captureCallback)](QImage &&image) {
QSize smallImageSize = image.size().scaled(QSize{96, 96}.boundedTo(image.size()),
Qt::KeepAspectRatio);
QImage smallImage = image.isNull() ? QImage{} : image.scaled(smallImageSize);
captureCallback(std::move(image), std::move(smallImage));
};
m_connectionManager.setCallback(std::move(callback));
nodeInstanceView.setTarget(m_target.data());
nodeInstanceView.setCrashCallback(abortCallback);
@@ -115,6 +124,20 @@ void ImageCacheCollector::start(Utils::SmallStringView name,
abortCallback();
}
std::pair<QImage, QImage> ImageCacheCollector::createImage(Utils::SmallStringView filePath,
Utils::SmallStringView state,
const ImageCache::AuxiliaryData &auxiliaryData)
{
return {};
}
QIcon ImageCacheCollector::createIcon(Utils::SmallStringView filePath,
Utils::SmallStringView state,
const ImageCache::AuxiliaryData &auxiliaryData)
{
return {};
}
void ImageCacheCollector::setTarget(ProjectExplorer::Target *target)
{
m_target = target;

View File

@@ -54,9 +54,18 @@ public:
void start(Utils::SmallStringView filePath,
Utils::SmallStringView state,
const ImageCache::AuxiliaryData &auxiliaryData,
CaptureCallback captureCallback,
AbortCallback abortCallback) override;
std::pair<QImage, QImage> createImage(Utils::SmallStringView filePath,
Utils::SmallStringView state,
const ImageCache::AuxiliaryData &auxiliaryData) override;
QIcon createIcon(Utils::SmallStringView filePath,
Utils::SmallStringView state,
const ImageCache::AuxiliaryData &auxiliaryData) override;
void setTarget(ProjectExplorer::Target *target);
private:

View File

@@ -25,6 +25,7 @@
#pragma once
#include <imagecacheauxiliarydata.h>
#include <utils/smallstringview.h>
#include <QImage>
@@ -34,15 +35,26 @@ namespace QmlDesigner {
class ImageCacheCollectorInterface
{
public:
using CaptureCallback = std::function<void(QImage &&image)>;
using CaptureCallback = std::function<void(QImage &&image, QImage &&smallImage)>;
using AbortCallback = std::function<void()>;
using ImagePair = std::pair<QImage, QImage>;
virtual void start(Utils::SmallStringView filePath,
Utils::SmallStringView state,
Utils::SmallStringView extraId,
const ImageCache::AuxiliaryData &auxiliaryData,
CaptureCallback captureCallback,
AbortCallback abortCallback)
= 0;
virtual ImagePair createImage(Utils::SmallStringView filePath,
Utils::SmallStringView extraId,
const ImageCache::AuxiliaryData &auxiliaryData)
= 0;
virtual QIcon createIcon(Utils::SmallStringView filePath,
Utils::SmallStringView extraId,
const ImageCache::AuxiliaryData &auxiliaryData)
= 0;
protected:
~ImageCacheCollectorInterface() = default;
};

View File

@@ -42,6 +42,8 @@ ImageCacheFontCollector::ImageCacheFontCollector() = default;
ImageCacheFontCollector::~ImageCacheFontCollector() = default;
namespace {
QByteArray fileToByteArray(QString const &filename)
{
QFile file(filename);
@@ -53,29 +55,17 @@ QByteArray fileToByteArray(QString const &filename)
return {};
}
} // namespace
void ImageCacheFontCollector::start(Utils::SmallStringView name,
Utils::SmallStringView state,
Utils::SmallStringView,
const ImageCache::AuxiliaryData &auxiliaryDataValue,
CaptureCallback captureCallback,
AbortCallback abortCallback)
{
// State contains size, text color, and sample text
QStringList hints = QString(state).split('@');
int dim(300);
if (hints.size() >= 1) {
bool ok = false;
int newDim = QString(hints[0]).toInt(&ok);
if (ok)
dim = newDim;
}
#ifndef QMLDESIGNER_TEST
QColor textColor(Theme::getColor(Theme::DStextColor));
#else
QColor textColor;
#endif
if (hints.size() >= 2)
textColor.setNamedColor(hints[1]);
QString text = hints.size() >= 3 ? hints[2] : "Abc";
QSize size(dim, dim);
auto &&auxiliaryData = std::get<ImageCache::FontCollectorSizeAuxiliaryData>(auxiliaryDataValue);
QColor textColor = auxiliaryData.colorName;
QSize size = auxiliaryData.size;
QRect rect({0, 0}, size);
QByteArray fontData(fileToByteArray(QString(name)));
@@ -86,8 +76,7 @@ void ImageCacheFontCollector::start(Utils::SmallStringView name,
const QStringList families = QFontDatabase::applicationFontFamilies(fontId);
if (!families.isEmpty()) {
QString fontFamily = families.first();
if (text.contains("%1"))
text = text.arg(fontFamily);
QString text = fontFamily + "\n\n" + auxiliaryData.text;
QFont font(fontFamily);
font.setStyle(rawFont.style());
font.setStyleName(rawFont.styleName());
@@ -120,7 +109,7 @@ void ImageCacheFontCollector::start(Utils::SmallStringView name,
painter.setFont(font);
painter.drawText(rect, flags, text);
captureCallback(std::move(image));
captureCallback(std::move(image), {});
return;
}
QFontDatabase::removeApplicationFont(fontId);
@@ -129,4 +118,133 @@ void ImageCacheFontCollector::start(Utils::SmallStringView name,
abortCallback();
}
std::pair<QImage, QImage> ImageCacheFontCollector::createImage(
Utils::SmallStringView name,
Utils::SmallStringView,
const ImageCache::AuxiliaryData &auxiliaryDataValue)
{
auto &&auxiliaryData = std::get<ImageCache::FontCollectorSizeAuxiliaryData>(auxiliaryDataValue);
QColor textColor = auxiliaryData.colorName;
QSize size = auxiliaryData.size;
QRect rect({0, 0}, size);
QByteArray fontData(fileToByteArray(QString(name)));
if (!fontData.isEmpty()) {
int fontId = QFontDatabase::addApplicationFontFromData(fontData);
if (fontId != -1) {
QRawFont rawFont(fontData, 10.); // Pixel size is irrelevant, we only need style/weight
const QStringList families = QFontDatabase::applicationFontFamilies(fontId);
if (!families.isEmpty()) {
QString fontFamily = families.first();
QString text = fontFamily + "\n\n" + auxiliaryData.text;
QFont font(fontFamily);
font.setStyle(rawFont.style());
font.setStyleName(rawFont.styleName());
font.setWeight(rawFont.weight());
QImage image(size, QImage::Format_ARGB32);
image.fill(Qt::transparent);
int pixelSize(200);
int flags = Qt::AlignCenter;
while (pixelSize >= 2) {
font.setPixelSize(pixelSize);
QFontMetrics fm(font, &image);
QRect bounds = fm.boundingRect(rect, flags, text);
if (bounds.width() < rect.width() && bounds.height() < rect.height()) {
break;
} else {
int newPixelSize = pixelSize - 1;
if (bounds.width() >= rect.width())
newPixelSize = int(qreal(pixelSize) * qreal(rect.width())
/ qreal(bounds.width()));
else if (bounds.height() >= rect.height())
newPixelSize = int(qreal(pixelSize) * qreal(rect.height())
/ qreal(bounds.height()));
if (newPixelSize < pixelSize)
pixelSize = newPixelSize;
else
--pixelSize;
}
}
QPainter painter(&image);
painter.setPen(textColor);
painter.setFont(font);
painter.drawText(rect, flags, text);
return {image, {}};
}
QFontDatabase::removeApplicationFont(fontId);
}
}
return {};
}
QIcon ImageCacheFontCollector::createIcon(Utils::SmallStringView name,
Utils::SmallStringView,
const ImageCache::AuxiliaryData &auxiliaryDataValue)
{
auto &&auxiliaryData = std::get<ImageCache::FontCollectorSizesAuxiliaryData>(auxiliaryDataValue);
QColor textColor = auxiliaryData.colorName;
auto sizes = auxiliaryData.sizes;
QIcon icon;
QByteArray fontData(fileToByteArray(QString(name)));
if (!fontData.isEmpty()) {
int fontId = QFontDatabase::addApplicationFontFromData(fontData);
if (fontId != -1) {
QRawFont rawFont(fontData, 10.); // Pixel size is irrelevant, we only need style/weight
const QStringList families = QFontDatabase::applicationFontFamilies(fontId);
if (!families.isEmpty()) {
QString fontFamily = families.first();
QString text = auxiliaryData.text;
QFont font(fontFamily);
font.setStyle(rawFont.style());
font.setStyleName(rawFont.styleName());
font.setWeight(rawFont.weight());
for (QSize size : sizes) {
QPixmap pixmap(size);
pixmap.fill(Qt::transparent);
int pixelSize(200);
int flags = Qt::AlignCenter;
QRect rect({0, 0}, size);
while (pixelSize >= 2) {
font.setPixelSize(pixelSize);
QFontMetrics fm(font, &pixmap);
QRect bounds = fm.boundingRect(rect, flags, text);
if (bounds.width() < rect.width() && bounds.height() < rect.height()) {
break;
} else {
int newPixelSize = pixelSize - 1;
if (bounds.width() >= rect.width())
newPixelSize = int(qreal(pixelSize) * qreal(rect.width())
/ qreal(bounds.width()));
else if (bounds.height() >= rect.height())
newPixelSize = int(qreal(pixelSize) * qreal(rect.height())
/ qreal(bounds.height()));
if (newPixelSize < pixelSize)
pixelSize = newPixelSize;
else
--pixelSize;
}
}
QPainter painter(&pixmap);
painter.setPen(textColor);
painter.setFont(font);
painter.drawText(rect, flags, text);
icon.addPixmap(pixmap);
}
} else {
QFontDatabase::removeApplicationFont(fontId);
}
}
}
return icon;
}
} // namespace QmlDesigner

View File

@@ -37,9 +37,18 @@ public:
~ImageCacheFontCollector();
void start(Utils::SmallStringView filePath,
Utils::SmallStringView state,
Utils::SmallStringView extraId,
const ImageCache::AuxiliaryData &auxiliaryData,
CaptureCallback captureCallback,
AbortCallback abortCallback) override;
std::pair<QImage, QImage> createImage(Utils::SmallStringView filePath,
Utils::SmallStringView extraId,
const ImageCache::AuxiliaryData &auxiliaryData) override;
QIcon createIcon(Utils::SmallStringView filePath,
Utils::SmallStringView extraId,
const ImageCache::AuxiliaryData &auxiliaryData) override;
};
} // namespace QmlDesigner

View File

@@ -47,17 +47,18 @@ ImageCacheGenerator::~ImageCacheGenerator()
waitForFinished();
}
void ImageCacheGenerator::generateImage(
Utils::SmallStringView name,
Utils::SmallStringView state,
Sqlite::TimeStamp timeStamp,
ImageCacheGeneratorInterface::CaptureCallback &&captureCallback,
AbortCallback &&abortCallback)
void ImageCacheGenerator::generateImage(Utils::SmallStringView name,
Utils::SmallStringView extraId,
Sqlite::TimeStamp timeStamp,
ImageCacheGeneratorInterface::CaptureCallback &&captureCallback,
AbortCallback &&abortCallback,
ImageCache::AuxiliaryData &&auxiliaryData)
{
{
std::lock_guard lock{m_mutex};
m_tasks.emplace_back(name,
state,
extraId,
std::move(auxiliaryData),
timeStamp,
std::move(captureCallback),
std::move(abortCallback));
@@ -82,6 +83,12 @@ void ImageCacheGenerator::waitForFinished()
if (m_backgroundThread)
m_backgroundThread->wait();
}
namespace {
Utils::PathString createId(Utils::SmallStringView name, Utils::SmallStringView extraId)
{
return extraId.empty() ? Utils::PathString{name} : Utils::PathString{name, "+", extraId};
}
} // namespace
void ImageCacheGenerator::startGeneration()
{
@@ -105,18 +112,22 @@ void ImageCacheGenerator::startGeneration()
m_collector.start(
task.filePath,
task.state,
[this, task](QImage &&image) {
task.extraId,
std::move(task.auxiliaryData),
[this, task](QImage &&image, QImage &&smallImage) {
if (image.isNull())
task.abortCallback();
else
task.captureCallback(image);
task.captureCallback(image, smallImage);
m_storage.storeImage(std::move(task.filePath), task.timeStamp, image);
m_storage.storeImage(createId(task.filePath, task.extraId),
task.timeStamp,
image,
smallImage);
},
[this, task] {
task.abortCallback();
m_storage.storeImage(std::move(task.filePath), task.timeStamp, {});
m_storage.storeImage(createId(task.filePath, task.extraId), task.timeStamp, {}, {});
});
std::lock_guard lock{m_mutex};

View File

@@ -27,6 +27,7 @@
#include "imagecachegeneratorinterface.h"
#include <imagecacheauxiliarydata.h>
#include <utils/smallstring.h>
#include <QThread>
@@ -51,10 +52,11 @@ public:
~ImageCacheGenerator();
void generateImage(Utils::SmallStringView filePath,
Utils::SmallStringView state,
Utils::SmallStringView extraId,
Sqlite::TimeStamp timeStamp,
CaptureCallback &&captureCallback,
AbortCallback &&abortCallback) override;
AbortCallback &&abortCallback,
ImageCache::AuxiliaryData &&auxiliaryData) override;
void clean() override;
void waitForFinished() override;
@@ -64,19 +66,22 @@ private:
{
Task() = default;
Task(Utils::SmallStringView filePath,
Utils::SmallStringView state,
Utils::SmallStringView extraId,
ImageCache::AuxiliaryData &&auxiliaryData,
Sqlite::TimeStamp timeStamp,
CaptureCallback &&captureCallback,
AbortCallback &&abortCallback)
: filePath(filePath)
, state(std::move(state))
, extraId(std::move(extraId))
, auxiliaryData(std::move(auxiliaryData))
, captureCallback(std::move(captureCallback))
, abortCallback(std::move(abortCallback))
, timeStamp(timeStamp)
{}
Utils::PathString filePath;
Utils::SmallString state;
Utils::SmallString extraId;
ImageCache::AuxiliaryData auxiliaryData;
CaptureCallback captureCallback;
AbortCallback abortCallback;
Sqlite::TimeStamp timeStamp;

View File

@@ -25,6 +25,7 @@
#pragma once
#include <imagecacheauxiliarydata.h>
#include <sqlitetimestamp.h>
#include <utils/smallstringview.h>
@@ -35,14 +36,15 @@ namespace QmlDesigner {
class ImageCacheGeneratorInterface
{
public:
using CaptureCallback = std::function<void(const QImage &image)>;
using CaptureCallback = std::function<void(const QImage &image, const QImage &smallImage)>;
using AbortCallback = std::function<void()>;
virtual void generateImage(Utils::SmallStringView name,
Utils::SmallStringView state,
Utils::SmallStringView extraId,
Sqlite::TimeStamp timeStamp,
CaptureCallback &&captureCallback,
AbortCallback &&abortCallback)
AbortCallback &&abortCallback,
ImageCache::AuxiliaryData &&auxiliaryData)
= 0;
virtual void clean() = 0;

View File

@@ -34,8 +34,8 @@
#include <sqlitewritestatement.h>
#include <QBuffer>
#include <QImageReader>
#include <QImageWriter>
#include <QIcon>
#include <QImage>
namespace QmlDesigner {
@@ -52,7 +52,7 @@ public:
transaction.commit();
}
Entry fetchImage(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const override
ImageEntry fetchImage(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const override
{
try {
Sqlite::DeferredTransaction transaction{database};
@@ -62,21 +62,37 @@ public:
transaction.commit();
if (optionalBlob) {
QBuffer buffer{&optionalBlob->byteArray};
QImageReader reader{&buffer, "PNG"};
return Entry{reader.read(), true};
}
if (optionalBlob)
return {readImage(optionalBlob->byteArray), true};
return {};
} catch (const Sqlite::StatementIsBusy &) {
return fetchImage(name, minimumTimeStamp);
}
}
Entry fetchIcon(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const override
ImageEntry fetchSmallImage(Utils::SmallStringView name,
Sqlite::TimeStamp minimumTimeStamp) const override
{
try {
Sqlite::DeferredTransaction transaction{database};
auto optionalBlob = selectSmallImageStatement.template value<Sqlite::ByteArrayBlob>(
name, minimumTimeStamp.value);
transaction.commit();
if (optionalBlob)
return ImageEntry{readImage(optionalBlob->byteArray), true};
return {};
} catch (const Sqlite::StatementIsBusy &) {
return fetchSmallImage(name, minimumTimeStamp);
}
}
IconEntry fetchIcon(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const override
{
try {
Sqlite::DeferredTransaction transaction{database};
@@ -86,12 +102,8 @@ public:
transaction.commit();
if (optionalBlob) {
QBuffer buffer{&optionalBlob->byteArray};
QImageReader reader{&buffer, "PNG"};
return Entry{reader.read(), true};
}
if (optionalBlob)
return {readIcon(optionalBlob->byteArray), true};
return {};
@@ -100,29 +112,40 @@ public:
}
}
void storeImage(Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QImage &image) override
void storeImage(Utils::SmallStringView name,
Sqlite::TimeStamp newTimeStamp,
const QImage &image,
const QImage &smallImage) 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()});
}
auto imageBuffer = createBuffer(image);
auto smallImageBuffer = createBuffer(smallImage);
upsertImageStatement.write(name,
newTimeStamp.value,
createBlobView(imageBuffer.get()),
createBlobView(smallImageBuffer.get()));
transaction.commit();
} catch (const Sqlite::StatementIsBusy &) {
return storeImage(name, newTimeStamp, image);
return storeImage(name, newTimeStamp, image, smallImage);
}
}
void storeIcon(Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QIcon &icon)
{
try {
Sqlite::ImmediateTransaction transaction{database};
auto iconBuffer = createBuffer(icon);
upsertIconStatement.write(name, newTimeStamp.value, createBlobView(iconBuffer.get()));
transaction.commit();
} catch (const Sqlite::StatementIsBusy &) {
return storeIcon(name, newTimeStamp, icon);
}
}
@@ -156,41 +179,113 @@ private:
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);
Sqlite::Table imageTable;
imageTable.setUseIfNotExists(true);
imageTable.setName("images");
imageTable.addColumn("id", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}});
imageTable.addColumn("name",
Sqlite::ColumnType::Text,
{Sqlite::NotNull{}, Sqlite::Unique{}});
imageTable.addColumn("mtime", Sqlite::ColumnType::Integer);
imageTable.addColumn("image", Sqlite::ColumnType::Blob);
imageTable.addColumn("smallImage", Sqlite::ColumnType::Blob);
table.initialize(database);
imageTable.initialize(database);
Sqlite::Table iconTable;
iconTable.setUseIfNotExists(true);
iconTable.setName("icons");
iconTable.addColumn("id", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}});
iconTable.addColumn("name",
Sqlite::ColumnType::Text,
{Sqlite::NotNull{}, Sqlite::Unique{}});
iconTable.addColumn("mtime", Sqlite::ColumnType::Integer);
iconTable.addColumn("icon", Sqlite::ColumnType::Blob);
iconTable.initialize(database);
}
};
std::unique_ptr<QBuffer> createImageBuffer(const QImage &image)
Sqlite::BlobView createBlobView(QBuffer *buffer)
{
if (buffer)
return Sqlite::BlobView{buffer->data()};
return {};
}
static std::unique_ptr<QBuffer> createBuffer(const QImage &image)
{
if (image.isNull())
return {};
auto buffer = std::make_unique<QBuffer>();
buffer->open(QIODevice::WriteOnly);
QImageWriter writer{buffer.get(), "PNG"};
writer.write(image);
QDataStream out{buffer.get()};
out << image;
return buffer;
}
static std::unique_ptr<QBuffer> createBuffer(const QIcon &icon)
{
if (icon.isNull())
return {};
auto buffer = std::make_unique<QBuffer>();
buffer->open(QIODevice::WriteOnly);
QDataStream out{buffer.get()};
out << icon;
return buffer;
}
static QIcon readIcon(const QByteArray &byteArray)
{
QIcon icon;
QBuffer buffer;
buffer.setData(byteArray);
buffer.open(QIODevice::ReadOnly);
QDataStream in{&buffer};
in >> icon;
return icon;
}
static QImage readImage(const QByteArray &byteArray)
{
QImage image;
QBuffer buffer;
buffer.setData(byteArray);
buffer.open(QIODevice::ReadOnly);
QDataStream in{&buffer};
in >> image;
return image;
}
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 selectSmallImageStatement{
"SELECT smallImage FROM images WHERE name=?1 AND mtime >= ?2", database};
mutable ReadStatement selectIconStatement{
"SELECT icon FROM images WHERE name=?1 AND mtime >= ?2", database};
"SELECT icon FROM icons WHERE name=?1 AND mtime >= ?2", database};
WriteStatement upsertImageStatement{
"INSERT INTO images(name, mtime, image, icon) VALUES (?1, ?2, ?3, ?4) ON "
"INSERT INTO images(name, mtime, image, smallImage) VALUES (?1, ?2, ?3, ?4) ON "
"CONFLICT(name) DO UPDATE SET mtime=excluded.mtime, image=excluded.image, "
"icon=excluded.icon",
"smallImage=excluded.smallImage",
database};
WriteStatement upsertIconStatement{
"INSERT INTO icons(name, mtime, icon) VALUES (?1, ?2, ?3) ON "
"CONFLICT(name) DO UPDATE SET mtime=excluded.mtime, icon=excluded.icon",
database};
};

View File

@@ -25,6 +25,7 @@
#pragma once
#include <QIcon>
#include <QImage>
#include <sqlitetimestamp.h>
@@ -32,23 +33,40 @@
namespace QmlDesigner {
namespace Internal {
class ImageCacheStorageEntry
class ImageCacheStorageImageEntry
{
public:
public:
QImage image;
bool hasEntry = false;
};
class ImageCacheStorageIconEntry
{
public:
QIcon icon;
bool hasEntry = false;
};
} // namespace Internal
class ImageCacheStorageInterface
{
public:
using Entry = Internal::ImageCacheStorageEntry;
using ImageEntry = Internal::ImageCacheStorageImageEntry;
using IconEntry = Internal::ImageCacheStorageIconEntry;
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 ImageEntry fetchImage(Utils::SmallStringView name,
Sqlite::TimeStamp minimumTimeStamp) const = 0;
virtual ImageEntry fetchSmallImage(Utils::SmallStringView name,
Sqlite::TimeStamp minimumTimeStamp) const = 0;
virtual IconEntry fetchIcon(Utils::SmallStringView name,
Sqlite::TimeStamp minimumTimeStamp) const = 0;
virtual void storeImage(Utils::SmallStringView name,
Sqlite::TimeStamp newTimeStamp,
const QImage &image,
const QImage &smallImage)
= 0;
virtual void storeIcon(Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QIcon &icon) = 0;
virtual void walCheckpointFull() = 0;
protected:

View File

@@ -0,0 +1,101 @@
/****************************************************************************
**
** Copyright (C) 2021 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 "synchronousimagecache.h"
#include "imagecachecollectorinterface.h"
#include "imagecachestorage.h"
#include "timestampprovider.h"
#include <thread>
namespace QmlDesigner {
namespace {
Utils::PathString createId(Utils::PathString filePath, Utils::SmallString extraId)
{
return extraId.empty() ? Utils::PathString{filePath} : Utils::PathString{filePath, "+", extraId};
}
} // namespace
QImage SynchronousImageCache::image(Utils::PathString filePath,
Utils::SmallString extraId,
const ImageCache::AuxiliaryData &auxiliaryData)
{
const auto id = createId(filePath, extraId);
const auto timeStamp = m_timeStampProvider.timeStamp(filePath);
const auto entry = m_storage.fetchImage(id, timeStamp);
if (entry.hasEntry)
return entry.image;
const auto &[image, smallImage] = m_collector.createImage(filePath, extraId, auxiliaryData);
m_storage.storeImage(id, timeStamp, image, smallImage);
return image;
}
QImage SynchronousImageCache::smallImage(Utils::PathString filePath,
Utils::SmallString extraId,
const ImageCache::AuxiliaryData &auxiliaryData)
{
const auto id = createId(filePath, extraId);
const auto timeStamp = m_timeStampProvider.timeStamp(filePath);
const auto entry = m_storage.fetchSmallImage(id, timeStamp);
if (entry.hasEntry)
return entry.image;
const auto &[image, smallImage] = m_collector.createImage(filePath, extraId, auxiliaryData);
m_storage.storeImage(id, timeStamp, image, smallImage);
return smallImage;
}
QIcon SynchronousImageCache::icon(Utils::PathString filePath,
Utils::SmallString extraId,
const ImageCache::AuxiliaryData &auxiliaryData)
{
const auto id = createId(filePath, extraId);
const auto timeStamp = m_timeStampProvider.timeStamp(filePath);
const auto entry = m_storage.fetchIcon(id, timeStamp);
if (entry.hasEntry)
return entry.icon;
const auto icon = m_collector.createIcon(filePath, extraId, auxiliaryData);
m_storage.storeIcon(id, timeStamp, icon);
return icon;
}
} // namespace QmlDesigner

View File

@@ -25,7 +25,7 @@
#pragma once
#include "imagecacheinterface.h"
#include "asynchronousimagecacheinterface.h"
#include <condition_variable>
#include <functional>
@@ -37,70 +37,78 @@ namespace QmlDesigner {
class TimeStampProviderInterface;
class ImageCacheStorageInterface;
class ImageCacheGeneratorInterface;
class ImageCacheCollectorInterface;
class ImageCache final : public ImageCacheInterface
class AsynchronousImageCache final : public AsynchronousImageCacheInterface
{
public:
using CaptureCallback = std::function<void(const QImage &)>;
using CaptureImageCallback = std::function<void(const QImage &)>;
using AbortCallback = std::function<void()>;
~ImageCache();
~AsynchronousImageCache();
ImageCache(ImageCacheStorageInterface &storage,
ImageCacheGeneratorInterface &generator,
TimeStampProviderInterface &timeStampProvider);
AsynchronousImageCache(ImageCacheStorageInterface &storage,
ImageCacheGeneratorInterface &generator,
TimeStampProviderInterface &timeStampProvider);
void requestImage(Utils::PathString name,
CaptureCallback captureCallback,
CaptureImageCallback captureCallback,
AbortCallback abortCallback,
Utils::SmallString state = {}) override;
void requestIcon(Utils::PathString name,
CaptureCallback captureCallback,
AbortCallback abortCallback,
Utils::SmallString state = {}) override;
Utils::SmallString extraId = {},
ImageCache::AuxiliaryData auxiliaryData = {}) override;
void requestSmallImage(Utils::PathString name,
CaptureImageCallback captureCallback,
AbortCallback abortCallback,
Utils::SmallString extraId = {},
ImageCache::AuxiliaryData auxiliaryData = {}) override;
void clean();
void waitForFinished();
private:
enum class RequestType { Image, Icon };
enum class RequestType { Image, SmallImage, Icon };
struct Entry
{
Entry() = default;
Entry(Utils::PathString name,
Utils::SmallString state,
CaptureCallback &&captureCallback,
Utils::SmallString extraId,
CaptureImageCallback &&captureCallback,
AbortCallback &&abortCallback,
ImageCache::AuxiliaryData &&auxiliaryData,
RequestType requestType)
: name{std::move(name)}
, state{std::move(state)}
, extraId{std::move(extraId)}
, captureCallback{std::move(captureCallback)}
, abortCallback{std::move(abortCallback)}
, auxiliaryData{std::move(auxiliaryData)}
, requestType{requestType}
{}
Utils::PathString name;
Utils::SmallString state;
CaptureCallback captureCallback;
Utils::SmallString extraId;
CaptureImageCallback captureCallback;
AbortCallback abortCallback;
ImageCache::AuxiliaryData auxiliaryData;
RequestType requestType = RequestType::Image;
};
std::tuple<bool, Entry> getEntry();
void addEntry(Utils::PathString &&name,
Utils::SmallString &&state,
CaptureCallback &&captureCallback,
Utils::SmallString &&extraId,
CaptureImageCallback &&captureCallback,
AbortCallback &&abortCallback,
ImageCache::AuxiliaryData &&auxiliaryData,
RequestType requestType);
void clearEntries();
void waitForEntries();
void stopThread();
bool isRunning();
static void request(Utils::SmallStringView name,
Utils::SmallStringView state,
ImageCache::RequestType requestType,
ImageCache::CaptureCallback captureCallback,
ImageCache::AbortCallback abortCallback,
Utils::SmallStringView extraId,
AsynchronousImageCache::RequestType requestType,
AsynchronousImageCache::CaptureImageCallback captureCallback,
AsynchronousImageCache::AbortCallback abortCallback,
ImageCache::AuxiliaryData auxiliaryData,
ImageCacheStorageInterface &storage,
ImageCacheGeneratorInterface &generator,
TimeStampProviderInterface &timeStampProvider);

View File

@@ -25,13 +25,15 @@
#pragma once
#include "imagecacheauxiliarydata.h"
#include <utils/smallstring.h>
#include <QImage>
namespace QmlDesigner {
class ImageCacheInterface
class AsynchronousImageCacheInterface
{
public:
using CaptureCallback = std::function<void(const QImage &)>;
@@ -40,18 +42,20 @@ public:
virtual void requestImage(Utils::PathString name,
CaptureCallback captureCallback,
AbortCallback abortCallback,
Utils::SmallString state = {})
Utils::SmallString extraId = {},
ImageCache::AuxiliaryData auxiliaryData = {})
= 0;
virtual void requestIcon(Utils::PathString name,
CaptureCallback captureCallback,
AbortCallback abortCallback,
Utils::SmallString state = {})
virtual void requestSmallImage(Utils::PathString name,
CaptureCallback captureCallback,
AbortCallback abortCallback,
Utils::SmallString extraId = {},
ImageCache::AuxiliaryData auxiliaryData = {})
= 0;
void clean();
protected:
~ImageCacheInterface() = default;
~AsynchronousImageCacheInterface() = default;
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,61 @@
/****************************************************************************
**
** Copyright (C) 2021 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/span.h>
#include <utils/variant.h>
#include <QSize>
#include <QString>
namespace QmlDesigner {
namespace ImageCache {
class NullAuxiliaryData
{};
class FontCollectorSizeAuxiliaryData
{
public:
QSize size;
QString colorName;
QString text;
};
class FontCollectorSizesAuxiliaryData
{
public:
Utils::span<const QSize> sizes;
QString colorName;
QString text;
};
using AuxiliaryData = std::variant<NullAuxiliaryData, FontCollectorSizeAuxiliaryData, FontCollectorSizesAuxiliaryData>;
} // namespace ImageCache
} // namespace QmlDesigner

View File

@@ -0,0 +1,76 @@
/****************************************************************************
**
** Copyright (C) 2021 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 "imagecacheauxiliarydata.h"
#include <utils/smallstring.h>
#include <QImage>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <thread>
namespace QmlDesigner {
class TimeStampProviderInterface;
class ImageCacheStorageInterface;
class ImageCacheGeneratorInterface;
class ImageCacheCollectorInterface;
class SynchronousImageCache
{
public:
using CaptureImageCallback = std::function<void(const QImage &)>;
using AbortCallback = std::function<void()>;
SynchronousImageCache(ImageCacheStorageInterface &storage,
TimeStampProviderInterface &timeStampProvider,
ImageCacheCollectorInterface &collector)
: m_storage(storage)
, m_timeStampProvider(timeStampProvider)
, m_collector(collector)
{}
QImage image(Utils::PathString filePath,
Utils::SmallString extraId = {},
const ImageCache::AuxiliaryData &auxiliaryData = {});
QImage smallImage(Utils::PathString filePath,
Utils::SmallString extraId = {},
const ImageCache::AuxiliaryData &auxiliaryData = {});
QIcon icon(Utils::PathString filePath,
Utils::SmallString extraId = {},
const ImageCache::AuxiliaryData &auxiliaryData = {});
private:
ImageCacheStorageInterface &m_storage;
TimeStampProviderInterface &m_timeStampProvider;
ImageCacheCollectorInterface &m_collector;
};
} // namespace QmlDesigner

View File

@@ -47,7 +47,7 @@ class Edit3DView;
namespace Internal { class DesignModeWidget; }
class ViewManagerData;
class ImageCache;
class AsynchronousImageCache;
class QMLDESIGNERCORE_EXPORT ViewManager
{
@@ -104,7 +104,7 @@ public:
void disableStandardViews();
void enableStandardViews();
ImageCache &imageCache();
AsynchronousImageCache &imageCache();
private: // functions
Q_DISABLE_COPY(ViewManager)

View File

@@ -453,7 +453,7 @@ void ViewManager::enableStandardViews()
attachViewsExceptRewriterAndComponetView();
}
ImageCache &ViewManager::imageCache()
AsynchronousImageCache &ViewManager::imageCache()
{
return d->itemLibraryView.imageCache();
}

View File

@@ -567,7 +567,7 @@ void QmlDesignerPlugin::emitUsageStatisticsContextAction(const QString &identifi
emitUsageStatistics(Constants::EVENT_ACTION_EXECUTED + identifier);
}
ImageCache &QmlDesignerPlugin::imageCache()
AsynchronousImageCache &QmlDesignerPlugin::imageCache()
{
return m_instance->d->viewManager.imageCache();
}

View File

@@ -42,7 +42,7 @@ namespace Core {
namespace QmlDesigner {
class QmlDesignerPluginPrivate;
class ImageCache;
class AsynchronousImageCache;
namespace Internal { class DesignModeWidget; }
@@ -87,7 +87,7 @@ public:
static void emitUsageStatisticsContextAction(const QString &identifier);
static void emitUsageStatisticsTime(const QString &identifier, int elapsed);
static ImageCache &imageCache();
static AsynchronousImageCache &imageCache();
signals:
void usageStatisticsNotifier(const QString &identifier);

View File

@@ -414,13 +414,15 @@ Project {
"pluginmanager/widgetpluginmanager.h",
"pluginmanager/widgetpluginpath.cpp",
"pluginmanager/widgetpluginpath.h",
"include/imagecache.h",
"include/imagecacheinterface.h",
"include/asynchronousimagecache.h",
"include/synchronousimagecache.h",
"include/imagecacheauxiliarydata.h",
"include/asynchronousimagecacheinterface.h",
"imagecache/imagecachecollector.cpp",
"imagecache/imagecachecollector.h",
"imagecache/imagecachefontcollector.cpp",
"imagecache/imagecachefontcollector.h",
"imagecache/imagecache.cpp",
"imagecache/asynchronousimagecache.cpp",
"imagecache/imagecachecollectorinterface.h",
"imagecache/imagecacheconnectionmanager.cpp",
"imagecache/imagecacheconnectionmanager.h",

View File

@@ -32,7 +32,8 @@ SOURCES += \
$$PWD/designercore/model/annotation.cpp \
$$PWD/designercore/rewritertransaction.cpp \
$$PWD/components/listmodeleditor/listmodeleditormodel.cpp \
$$PWD/designercore/imagecache/imagecache.cpp \
$$PWD/designercore/imagecache/asynchronousimagecache.cpp \
$$PWD/designercore/imagecache/synchronousimagecache.cpp \
$$PWD/designercore/imagecache/imagecachegenerator.cpp
HEADERS += \
@@ -42,8 +43,10 @@ HEADERS += \
$$PWD/designercore/imagecache/imagecachestorageinterface.h \
$$PWD/designercore/imagecache/imagecachegeneratorinterface.h \
$$PWD/designercore/imagecache/timestampproviderinterface.h \
$$PWD/designercore/include/imagecache.h \
$$PWD/designercore/include/imagecacheinterface.h \
$$PWD/designercore/include/asynchronousimagecache.h \
$$PWD/designercore/include/synchronousimagecache.h \
$$PWD/designercore/include/imagecacheauxiliarydata.h \
$$PWD/designercore/include/asynchronousimagecacheinterface.h \
$$PWD/designercore/include/modelnode.h \
$$PWD/designercore/include/model.h \
$$PWD/../../../share/qtcreator/qml/qmlpuppet/interfaces/commondefines.h \