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 "async.h"
|
||||
#include "mimeutils.h"
|
||||
#include "movie.h"
|
||||
#include "networkaccessmanager.h"
|
||||
#include "stylehelper.h"
|
||||
#include "textutils.h"
|
||||
@@ -19,7 +20,6 @@
|
||||
#include <QBuffer>
|
||||
#include <QCache>
|
||||
#include <QGuiApplication>
|
||||
#include <QMovie>
|
||||
#include <QPainter>
|
||||
#include <QTextBlock>
|
||||
#include <QTextBrowser>
|
||||
@@ -145,7 +145,7 @@ public:
|
||||
class Entry
|
||||
{
|
||||
public:
|
||||
using Pointer = std::unique_ptr<Entry>;
|
||||
using Pointer = std::shared_ptr<Entry>;
|
||||
|
||||
Entry(const QByteArray &data)
|
||||
{
|
||||
@@ -154,10 +154,22 @@ public:
|
||||
|
||||
buffer.setData(data);
|
||||
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;
|
||||
QMovie movie;
|
||||
QtcMovie movie;
|
||||
};
|
||||
|
||||
public:
|
||||
@@ -176,19 +188,24 @@ public:
|
||||
{
|
||||
Q_UNUSED(doc);
|
||||
Q_UNUSED(posInDocument);
|
||||
QSize result = Utils::Icons::UNKNOWN_FILE.icon().actualSize(QSize(16, 16));
|
||||
QString name = format.toImageFormat().name();
|
||||
|
||||
Entry *entry = m_entries.object(name);
|
||||
|
||||
if (entry && entry->movie.isValid()) {
|
||||
if (!entry->movie.frameRect().isValid())
|
||||
entry->movie.jumpToFrame(0);
|
||||
return entry->movie.frameRect().size();
|
||||
} else if (!entry) {
|
||||
Entry::Pointer *entryPtr = m_entries.object(name);
|
||||
if (!entryPtr) {
|
||||
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(
|
||||
@@ -201,18 +218,15 @@ public:
|
||||
Q_UNUSED(document);
|
||||
Q_UNUSED(posInDocument);
|
||||
|
||||
Entry *entry = m_entries.object(format.toImageFormat().name());
|
||||
Entry::Pointer *entryPtr = m_entries.object(format.toImageFormat().name());
|
||||
|
||||
if (entry) {
|
||||
if (entry->movie.isValid())
|
||||
painter->drawImage(rect, entry->movie.currentImage());
|
||||
else
|
||||
painter->drawPixmap(rect.toRect(), m_brokenImage.pixmap(rect.size().toSize()));
|
||||
return;
|
||||
}
|
||||
|
||||
painter->drawPixmap(
|
||||
rect.toRect(), Utils::Icons::UNKNOWN_FILE.icon().pixmap(rect.size().toSize()));
|
||||
if (!entryPtr)
|
||||
painter->drawPixmap(
|
||||
rect.toRect(), Utils::Icons::UNKNOWN_FILE.icon().pixmap(rect.size().toSize()));
|
||||
else if (!(*entryPtr)->movie.isValid())
|
||||
painter->drawPixmap(rect.toRect(), m_brokenImage.pixmap(rect.size().toSize()));
|
||||
else
|
||||
painter->drawImage(rect, (*entryPtr)->movie.currentImage());
|
||||
}
|
||||
|
||||
void set(const QString &name, QByteArray data)
|
||||
@@ -220,17 +234,21 @@ public:
|
||||
if (data.size() > m_entries.maxCost())
|
||||
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) {
|
||||
connect(&entry->movie, &QMovie::frameChanged, this, [this]() { m_redraw(); });
|
||||
connect(&entry->movie, &QtcMovie::frameChanged, this, [this]() { m_redraw(); });
|
||||
entry->movie.start();
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -239,7 +257,7 @@ public:
|
||||
private:
|
||||
std::function<void()> m_redraw;
|
||||
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}});
|
||||
|
||||
@@ -292,57 +310,73 @@ public:
|
||||
if (!m_loadRemoteImages)
|
||||
remoteUrls.clear();
|
||||
|
||||
Storage<std::pair<QUrl, QByteArray>> remoteData;
|
||||
|
||||
const LoopList remoteIterator(Utils::toList(remoteUrls));
|
||||
const LoopList localIterator(Utils::toList(localUrls));
|
||||
|
||||
auto onQuerySetup = [this, remoteIterator, base = m_basePath.toUrl()](NetworkQuery &query) {
|
||||
QUrl url = *remoteIterator;
|
||||
if (url.isRelative())
|
||||
url = base.resolved(url);
|
||||
auto onQuerySetup =
|
||||
[remoteData, this, remoteIterator, base = m_basePath.toUrl()](NetworkQuery &query) {
|
||||
QUrl url = *remoteIterator;
|
||||
if (url.isRelative())
|
||||
url = base.resolved(url);
|
||||
|
||||
QNetworkRequest request(url);
|
||||
if (m_requestHook)
|
||||
m_requestHook(&request);
|
||||
QNetworkRequest request(url);
|
||||
if (m_requestHook)
|
||||
m_requestHook(&request);
|
||||
|
||||
query.setRequest(request);
|
||||
query.setNetworkAccessManager(m_networkAccessManager);
|
||||
query.setProperty("originalName", *remoteIterator);
|
||||
};
|
||||
query.setRequest(request);
|
||||
query.setNetworkAccessManager(m_networkAccessManager);
|
||||
remoteData->first = *remoteIterator;
|
||||
};
|
||||
|
||||
auto onQueryDone = [this](const NetworkQuery &query, DoneWith result) {
|
||||
auto onQueryDone = [this, remoteData](const NetworkQuery &query, DoneWith result) {
|
||||
if (result == DoneWith::Cancel)
|
||||
return;
|
||||
m_urlsToLoad.remove(query.reply()->url());
|
||||
|
||||
if (result == DoneWith::Success)
|
||||
m_imageHandler.set(query.property("originalName").toString(), query.reply()->readAll());
|
||||
else
|
||||
m_imageHandler.set(query.property("originalName").toString(), QByteArray{});
|
||||
|
||||
markContentsDirty(0, this->characterCount());
|
||||
if (result == DoneWith::Success) {
|
||||
remoteData->second = query.reply()->readAll();
|
||||
} else {
|
||||
m_imageHandler.set(remoteData->first.toString(), QByteArray{});
|
||||
markContentsDirty(0, this->characterCount());
|
||||
}
|
||||
};
|
||||
|
||||
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) {
|
||||
const FilePath u = basePath.resolvePath(localIterator->path());
|
||||
async.setConcurrentCallData(
|
||||
[](FilePath f) -> EntryPointer {
|
||||
[](QPromise<EntryPointer> &promise, const FilePath &f) {
|
||||
auto data = f.fileContents();
|
||||
if (!data)
|
||||
return nullptr;
|
||||
if (!data || promise.isCanceled())
|
||||
return;
|
||||
|
||||
return std::make_unique<AnimatedImageHandler::Entry>(*data);
|
||||
promise.addResult(std::make_shared<AnimatedImageHandler::Entry>(*data));
|
||||
},
|
||||
u);
|
||||
};
|
||||
|
||||
auto onLocalDone = [localIterator, this](const Async<EntryPointer> &async) {
|
||||
#if QT_VERSION > QT_VERSION_CHECK(6, 5, 2)
|
||||
EntryPointer result = async.takeResult();
|
||||
#else
|
||||
EntryPointer result = {};
|
||||
#endif
|
||||
EntryPointer result = async.result();
|
||||
if (result)
|
||||
m_imageHandler.set(localIterator->toString(), std::move(result));
|
||||
};
|
||||
@@ -351,7 +385,11 @@ public:
|
||||
Group group {
|
||||
parallelLimit(2),
|
||||
For(remoteIterator) >> Do {
|
||||
NetworkQueryTask{onQuerySetup, onQueryDone} || successItem,
|
||||
remoteData,
|
||||
Group {
|
||||
NetworkQueryTask{onQuerySetup, onQueryDone},
|
||||
AsyncTask<EntryPointer>(onMakeEntrySetup, onMakeEntryDone),
|
||||
} || successItem,
|
||||
},
|
||||
For(localIterator) >> Do {
|
||||
AsyncTask<EntryPointer>(onLocalSetup, onLocalDone) || successItem,
|
||||
@@ -372,7 +410,7 @@ public:
|
||||
void setBasePath(const FilePath &filePath) { m_basePath = filePath; }
|
||||
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 setMaximumCacheSize(qsizetype maxSize) { m_imageHandler.setMaximumCacheSize(maxSize); }
|
||||
|
||||
|
Reference in New Issue
Block a user