forked from qt-creator/qt-creator
Utils: Let MarkdownBrowser preload first frame async
Change-Id: Ib732874c27247dbe11cf6a1eda5c92d0ff678a3a Reviewed-by: Jarek Kobus <jaroslaw.kobus@qt.io>
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
#include "algorithm.h"
|
#include "algorithm.h"
|
||||||
#include "async.h"
|
#include "async.h"
|
||||||
#include "mimeutils.h"
|
#include "mimeutils.h"
|
||||||
|
#include "movie.h"
|
||||||
#include "networkaccessmanager.h"
|
#include "networkaccessmanager.h"
|
||||||
#include "stylehelper.h"
|
#include "stylehelper.h"
|
||||||
#include "textutils.h"
|
#include "textutils.h"
|
||||||
@@ -19,7 +20,6 @@
|
|||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
#include <QCache>
|
#include <QCache>
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#include <QMovie>
|
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QTextBlock>
|
#include <QTextBlock>
|
||||||
#include <QTextBrowser>
|
#include <QTextBrowser>
|
||||||
@@ -145,7 +145,7 @@ public:
|
|||||||
class Entry
|
class Entry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using Pointer = std::unique_ptr<Entry>;
|
using Pointer = std::shared_ptr<Entry>;
|
||||||
|
|
||||||
Entry(const QByteArray &data)
|
Entry(const QByteArray &data)
|
||||||
{
|
{
|
||||||
@@ -154,10 +154,22 @@ public:
|
|||||||
|
|
||||||
buffer.setData(data);
|
buffer.setData(data);
|
||||||
movie.setDevice(&buffer);
|
movie.setDevice(&buffer);
|
||||||
|
if (movie.isValid()) {
|
||||||
|
if (!movie.frameRect().isValid())
|
||||||
|
movie.jumpToFrame(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
moveToThread(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void moveToThread(QThread *thread)
|
||||||
|
{
|
||||||
|
buffer.moveToThread(thread);
|
||||||
|
movie.moveToThread(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
QBuffer buffer;
|
QBuffer buffer;
|
||||||
QMovie movie;
|
QtcMovie movie;
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -176,19 +188,24 @@ public:
|
|||||||
{
|
{
|
||||||
Q_UNUSED(doc);
|
Q_UNUSED(doc);
|
||||||
Q_UNUSED(posInDocument);
|
Q_UNUSED(posInDocument);
|
||||||
|
QSize result = Utils::Icons::UNKNOWN_FILE.icon().actualSize(QSize(16, 16));
|
||||||
QString name = format.toImageFormat().name();
|
QString name = format.toImageFormat().name();
|
||||||
|
|
||||||
Entry *entry = m_entries.object(name);
|
Entry::Pointer *entryPtr = m_entries.object(name);
|
||||||
|
if (!entryPtr) {
|
||||||
if (entry && entry->movie.isValid()) {
|
|
||||||
if (!entry->movie.frameRect().isValid())
|
|
||||||
entry->movie.jumpToFrame(0);
|
|
||||||
return entry->movie.frameRect().size();
|
|
||||||
} else if (!entry) {
|
|
||||||
m_scheduleLoad(name);
|
m_scheduleLoad(name);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Utils::Icons::UNKNOWN_FILE.icon().actualSize(QSize(16, 16));
|
Entry::Pointer entry = *entryPtr;
|
||||||
|
|
||||||
|
if (entry->movie.isValid()) {
|
||||||
|
if (!entry->movie.frameRect().isValid())
|
||||||
|
entry->movie.jumpToFrame(0);
|
||||||
|
result = entry->movie.frameRect().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawObject(
|
void drawObject(
|
||||||
@@ -201,18 +218,15 @@ public:
|
|||||||
Q_UNUSED(document);
|
Q_UNUSED(document);
|
||||||
Q_UNUSED(posInDocument);
|
Q_UNUSED(posInDocument);
|
||||||
|
|
||||||
Entry *entry = m_entries.object(format.toImageFormat().name());
|
Entry::Pointer *entryPtr = m_entries.object(format.toImageFormat().name());
|
||||||
|
|
||||||
if (entry) {
|
if (!entryPtr)
|
||||||
if (entry->movie.isValid())
|
painter->drawPixmap(
|
||||||
painter->drawImage(rect, entry->movie.currentImage());
|
rect.toRect(), Utils::Icons::UNKNOWN_FILE.icon().pixmap(rect.size().toSize()));
|
||||||
else
|
else if (!(*entryPtr)->movie.isValid())
|
||||||
painter->drawPixmap(rect.toRect(), m_brokenImage.pixmap(rect.size().toSize()));
|
painter->drawPixmap(rect.toRect(), m_brokenImage.pixmap(rect.size().toSize()));
|
||||||
return;
|
else
|
||||||
}
|
painter->drawImage(rect, (*entryPtr)->movie.currentImage());
|
||||||
|
|
||||||
painter->drawPixmap(
|
|
||||||
rect.toRect(), Utils::Icons::UNKNOWN_FILE.icon().pixmap(rect.size().toSize()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void set(const QString &name, QByteArray data)
|
void set(const QString &name, QByteArray data)
|
||||||
@@ -220,17 +234,21 @@ public:
|
|||||||
if (data.size() > m_entries.maxCost())
|
if (data.size() > m_entries.maxCost())
|
||||||
data.clear();
|
data.clear();
|
||||||
|
|
||||||
set(name, std::make_unique<Entry>(data));
|
set(name, std::make_shared<Entry>(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
void set(const QString &name, std::unique_ptr<Entry> entry)
|
void set(const QString &name, Entry::Pointer entry)
|
||||||
{
|
{
|
||||||
|
entry->moveToThread(thread());
|
||||||
|
|
||||||
if (entry->movie.frameCount() > 1) {
|
if (entry->movie.frameCount() > 1) {
|
||||||
connect(&entry->movie, &QMovie::frameChanged, this, [this]() { m_redraw(); });
|
connect(&entry->movie, &QtcMovie::frameChanged, this, [this]() { m_redraw(); });
|
||||||
entry->movie.start();
|
entry->movie.start();
|
||||||
}
|
}
|
||||||
const qint64 size = qMax(1, entry->buffer.size());
|
const qint64 size = qMax(1, entry->buffer.size());
|
||||||
if (m_entries.insert(name, entry.release(), size))
|
|
||||||
|
Entry::Pointer *entryPtr = new Entry::Pointer(entry);
|
||||||
|
if (m_entries.insert(name, entryPtr, size))
|
||||||
m_redraw();
|
m_redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,7 +257,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
std::function<void()> m_redraw;
|
std::function<void()> m_redraw;
|
||||||
std::function<void(const QString &)> m_scheduleLoad;
|
std::function<void(const QString &)> m_scheduleLoad;
|
||||||
QCache<QString, Entry> m_entries;
|
QCache<QString, Entry::Pointer> m_entries;
|
||||||
|
|
||||||
const Icon ErrorCloseIcon = Utils::Icon({{":/utils/images/close.png", Theme::IconsErrorColor}});
|
const Icon ErrorCloseIcon = Utils::Icon({{":/utils/images/close.png", Theme::IconsErrorColor}});
|
||||||
|
|
||||||
@@ -292,57 +310,73 @@ public:
|
|||||||
if (!m_loadRemoteImages)
|
if (!m_loadRemoteImages)
|
||||||
remoteUrls.clear();
|
remoteUrls.clear();
|
||||||
|
|
||||||
|
Storage<std::pair<QUrl, QByteArray>> remoteData;
|
||||||
|
|
||||||
const LoopList remoteIterator(Utils::toList(remoteUrls));
|
const LoopList remoteIterator(Utils::toList(remoteUrls));
|
||||||
const LoopList localIterator(Utils::toList(localUrls));
|
const LoopList localIterator(Utils::toList(localUrls));
|
||||||
|
|
||||||
auto onQuerySetup = [this, remoteIterator, base = m_basePath.toUrl()](NetworkQuery &query) {
|
auto onQuerySetup =
|
||||||
QUrl url = *remoteIterator;
|
[remoteData, this, remoteIterator, base = m_basePath.toUrl()](NetworkQuery &query) {
|
||||||
if (url.isRelative())
|
QUrl url = *remoteIterator;
|
||||||
url = base.resolved(url);
|
if (url.isRelative())
|
||||||
|
url = base.resolved(url);
|
||||||
|
|
||||||
QNetworkRequest request(url);
|
QNetworkRequest request(url);
|
||||||
if (m_requestHook)
|
if (m_requestHook)
|
||||||
m_requestHook(&request);
|
m_requestHook(&request);
|
||||||
|
|
||||||
query.setRequest(request);
|
query.setRequest(request);
|
||||||
query.setNetworkAccessManager(m_networkAccessManager);
|
query.setNetworkAccessManager(m_networkAccessManager);
|
||||||
query.setProperty("originalName", *remoteIterator);
|
remoteData->first = *remoteIterator;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto onQueryDone = [this](const NetworkQuery &query, DoneWith result) {
|
auto onQueryDone = [this, remoteData](const NetworkQuery &query, DoneWith result) {
|
||||||
if (result == DoneWith::Cancel)
|
if (result == DoneWith::Cancel)
|
||||||
return;
|
return;
|
||||||
m_urlsToLoad.remove(query.reply()->url());
|
m_urlsToLoad.remove(query.reply()->url());
|
||||||
|
|
||||||
if (result == DoneWith::Success)
|
if (result == DoneWith::Success) {
|
||||||
m_imageHandler.set(query.property("originalName").toString(), query.reply()->readAll());
|
remoteData->second = query.reply()->readAll();
|
||||||
else
|
} else {
|
||||||
m_imageHandler.set(query.property("originalName").toString(), QByteArray{});
|
m_imageHandler.set(remoteData->first.toString(), QByteArray{});
|
||||||
|
markContentsDirty(0, this->characterCount());
|
||||||
markContentsDirty(0, this->characterCount());
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
using EntryPointer = AnimatedImageHandler::Entry::Pointer;
|
using EntryPointer = AnimatedImageHandler::Entry::Pointer;
|
||||||
|
|
||||||
|
auto onMakeEntrySetup = [remoteData](Async<EntryPointer> &async) {
|
||||||
|
async.setConcurrentCallData(
|
||||||
|
[](const QByteArray &data) {
|
||||||
|
return std::make_shared<AnimatedImageHandler::Entry>(data);
|
||||||
|
},
|
||||||
|
remoteData->second);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto onMakeEntryDone =
|
||||||
|
[this, remoteIterator, remoteData](const Async<EntryPointer> &async) {
|
||||||
|
EntryPointer result = async.result();
|
||||||
|
if (result) {
|
||||||
|
m_imageHandler.set(remoteData->first.toString(), result);
|
||||||
|
markContentsDirty(0, this->characterCount());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
auto onLocalSetup = [localIterator, basePath = m_basePath](Async<EntryPointer> &async) {
|
auto onLocalSetup = [localIterator, basePath = m_basePath](Async<EntryPointer> &async) {
|
||||||
const FilePath u = basePath.resolvePath(localIterator->path());
|
const FilePath u = basePath.resolvePath(localIterator->path());
|
||||||
async.setConcurrentCallData(
|
async.setConcurrentCallData(
|
||||||
[](FilePath f) -> EntryPointer {
|
[](QPromise<EntryPointer> &promise, const FilePath &f) {
|
||||||
auto data = f.fileContents();
|
auto data = f.fileContents();
|
||||||
if (!data)
|
if (!data || promise.isCanceled())
|
||||||
return nullptr;
|
return;
|
||||||
|
|
||||||
return std::make_unique<AnimatedImageHandler::Entry>(*data);
|
promise.addResult(std::make_shared<AnimatedImageHandler::Entry>(*data));
|
||||||
},
|
},
|
||||||
u);
|
u);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto onLocalDone = [localIterator, this](const Async<EntryPointer> &async) {
|
auto onLocalDone = [localIterator, this](const Async<EntryPointer> &async) {
|
||||||
#if QT_VERSION > QT_VERSION_CHECK(6, 5, 2)
|
EntryPointer result = async.result();
|
||||||
EntryPointer result = async.takeResult();
|
|
||||||
#else
|
|
||||||
EntryPointer result = {};
|
|
||||||
#endif
|
|
||||||
if (result)
|
if (result)
|
||||||
m_imageHandler.set(localIterator->toString(), std::move(result));
|
m_imageHandler.set(localIterator->toString(), std::move(result));
|
||||||
};
|
};
|
||||||
@@ -351,7 +385,11 @@ public:
|
|||||||
Group group {
|
Group group {
|
||||||
parallelLimit(2),
|
parallelLimit(2),
|
||||||
For(remoteIterator) >> Do {
|
For(remoteIterator) >> Do {
|
||||||
NetworkQueryTask{onQuerySetup, onQueryDone} || successItem,
|
remoteData,
|
||||||
|
Group {
|
||||||
|
NetworkQueryTask{onQuerySetup, onQueryDone},
|
||||||
|
AsyncTask<EntryPointer>(onMakeEntrySetup, onMakeEntryDone),
|
||||||
|
} || successItem,
|
||||||
},
|
},
|
||||||
For(localIterator) >> Do {
|
For(localIterator) >> Do {
|
||||||
AsyncTask<EntryPointer>(onLocalSetup, onLocalDone) || successItem,
|
AsyncTask<EntryPointer>(onLocalSetup, onLocalDone) || successItem,
|
||||||
@@ -372,7 +410,7 @@ public:
|
|||||||
void setBasePath(const FilePath &filePath) { m_basePath = filePath; }
|
void setBasePath(const FilePath &filePath) { m_basePath = filePath; }
|
||||||
void setAllowRemoteImages(bool allow) { m_loadRemoteImages = allow; }
|
void setAllowRemoteImages(bool allow) { m_loadRemoteImages = allow; }
|
||||||
|
|
||||||
void setNetworkAccessManager(QNetworkAccessManager *nam) { m_networkAccessManager = nam;}
|
void setNetworkAccessManager(QNetworkAccessManager *nam) { m_networkAccessManager = nam; }
|
||||||
void setRequestHook(const MarkdownBrowser::RequestHook &hook) { m_requestHook = hook; }
|
void setRequestHook(const MarkdownBrowser::RequestHook &hook) { m_requestHook = hook; }
|
||||||
void setMaximumCacheSize(qsizetype maxSize) { m_imageHandler.setMaximumCacheSize(maxSize); }
|
void setMaximumCacheSize(qsizetype maxSize) { m_imageHandler.setMaximumCacheSize(maxSize); }
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user