diff --git a/src/libs/utils/markdownbrowser.cpp b/src/libs/utils/markdownbrowser.cpp index 58ab1a0259e..3f5fc11c65e 100644 --- a/src/libs/utils/markdownbrowser.cpp +++ b/src/libs/utils/markdownbrowser.cpp @@ -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 #include #include -#include #include #include #include @@ -145,7 +145,7 @@ public: class Entry { public: - using Pointer = std::unique_ptr; + using Pointer = std::shared_ptr; 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(data)); + set(name, std::make_shared(data)); } - void set(const QString &name, std::unique_ptr 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 m_redraw; std::function m_scheduleLoad; - QCache m_entries; + QCache m_entries; const Icon ErrorCloseIcon = Utils::Icon({{":/utils/images/close.png", Theme::IconsErrorColor}}); @@ -292,57 +310,73 @@ public: if (!m_loadRemoteImages) remoteUrls.clear(); + Storage> 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 &async) { + async.setConcurrentCallData( + [](const QByteArray &data) { + return std::make_shared(data); + }, + remoteData->second); + }; + + auto onMakeEntryDone = + [this, remoteIterator, remoteData](const Async &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 &async) { const FilePath u = basePath.resolvePath(localIterator->path()); async.setConcurrentCallData( - [](FilePath f) -> EntryPointer { + [](QPromise &promise, const FilePath &f) { auto data = f.fileContents(); - if (!data) - return nullptr; + if (!data || promise.isCanceled()) + return; - return std::make_unique(*data); + promise.addResult(std::make_shared(*data)); }, u); }; auto onLocalDone = [localIterator, this](const Async &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(onMakeEntrySetup, onMakeEntryDone), + } || successItem, }, For(localIterator) >> Do { AsyncTask(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); }