diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp index 36800708bfa..f1e54f1c599 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp @@ -37,6 +37,7 @@ QQuickImageResponse *ItemLibraryIconImageProvider::requestImageResponse(const QS [response, abortReason] { switch (abortReason) { case ImageCache::AbortReason::Failed: + case ImageCache::AbortReason::NoEntry: if (response) response->abort(); break; diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp index 3d53e894519..9590cc48565 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp @@ -60,10 +60,14 @@ void AsynchronousExplicitImageCache::request(Utils::SmallStringView name, }; const auto entry = requestImageFromStorage(requestType); - if (entry && !entry->isNull()) - captureCallback(*entry); - else - abortCallback(ImageCache::AbortReason::Failed); + if (entry) { + if (entry->isNull()) + abortCallback(ImageCache::AbortReason::Failed); + else + captureCallback(*entry); + } else { + abortCallback(ImageCache::AbortReason::NoEntry); + } } void AsynchronousExplicitImageCache::wait() diff --git a/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp b/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp index 97dda3f8bf4..0ad386a1253 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp @@ -27,16 +27,20 @@ QQuickImageResponse *ExplicitImageCacheImageProvider::requestImageResponse(const }, Qt::QueuedConnection); }, - [response = QPointer(response.get())]( - ImageCache::AbortReason abortReason) { + [response = QPointer(response.get()), + failedImage = m_failedImage](ImageCache::AbortReason abortReason) { QMetaObject::invokeMethod( response, - [response, abortReason] { + [response, abortReason, failedImage] { switch (abortReason) { - case ImageCache::AbortReason::Failed: + case ImageCache::AbortReason::NoEntry: if (response) response->abort(); break; + case ImageCache::AbortReason::Failed: + if (response) + response->setImage(failedImage); + break; case ImageCache::AbortReason::Abort: response->cancel(); break; diff --git a/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.h b/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.h index a1e2c19d2d6..7200f2c97b5 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.h +++ b/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.h @@ -14,9 +14,11 @@ class ExplicitImageCacheImageProvider : public QQuickAsyncImageProvider { public: ExplicitImageCacheImageProvider(AsynchronousExplicitImageCache &imageCache, - const QImage &defaultImage) + const QImage &defaultImage, + const QImage &failedImage) : m_cache(imageCache) , m_defaultImage(defaultImage) + , m_failedImage(failedImage) {} QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override; @@ -24,6 +26,7 @@ public: private: AsynchronousExplicitImageCache &m_cache; QImage m_defaultImage; + QImage m_failedImage; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.cpp b/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.cpp index 2266e37bb92..fd37f02f044 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.cpp @@ -32,6 +32,7 @@ QQuickImageResponse *MidSizeImageCacheProvider::requestImageResponse(const QStri [response, abortReason] { switch (abortReason) { case ImageCache::AbortReason::Failed: + case ImageCache::AbortReason::NoEntry: if (response) response->abort(); break; diff --git a/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp b/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp index 927a9415834..d095fad07ea 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp @@ -33,6 +33,7 @@ QQuickImageResponse *SmallImageCacheProvider::requestImageResponse(const QString [response, abortReason] { switch (abortReason) { case ImageCache::AbortReason::Failed: + case ImageCache::AbortReason::NoEntry: if (response) response->abort(); break; diff --git a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h index 4ff8cf0b2c1..36495e9361c 100644 --- a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h +++ b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h @@ -43,7 +43,7 @@ using AuxiliaryData = std::variant; -enum class AbortReason : char { Abort, Failed }; +enum class AbortReason : char { Abort, Failed, NoEntry }; using CaptureImageCallback = std::function; using CaptureImageWithScaledImagesCallback = std::function< diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index 46a5e925b8b..b2593adf749 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -54,11 +54,16 @@ ProjectExplorer::Target *activeTarget(ProjectExplorer::Project *project) return {}; } -QString defaultImagePath() +QString previewDefaultImagePath() { return Core::ICore::resourcePath("qmldesigner/welcomepage/images/newThumbnail.png").toString(); } +QString previewBrokenImagePath() +{ + return Core::ICore::resourcePath("qmldesigner/welcomepage/images/newPreview.png").toString(); +} + ::QmlProjectManager::QmlBuildSystem *getQmlBuildSystem(::ProjectExplorer::Target *target) { return qobject_cast<::QmlProjectManager::QmlBuildSystem *>(target->buildSystem()); @@ -147,7 +152,7 @@ public: QSize{300, 300}, QSize{1000, 1000}, externalDependencies, - ImageCacheCollectorNullImageHandling::DontCaptureNullImage} + ImageCacheCollectorNullImageHandling::CaptureNullImage} { timer.setSingleShot(true); } @@ -260,8 +265,10 @@ QmlDesignerProjectManager::~QmlDesignerProjectManager() = default; void QmlDesignerProjectManager::registerPreviewImageProvider(QQmlEngine *engine) const { - auto imageProvider = std::make_unique(m_previewImageCacheData->cache, - QImage{defaultImagePath()}); + auto imageProvider = std::make_unique( + m_previewImageCacheData->cache, + QImage{previewDefaultImagePath()}, + QImage{previewBrokenImagePath()}); engine->addImageProvider("project_preview", imageProvider.release()); } diff --git a/tests/unit/unittest/asynchronousexplicitimagecache-test.cpp b/tests/unit/unittest/asynchronousexplicitimagecache-test.cpp index 0502138c33f..83f1b961e62 100644 --- a/tests/unit/unittest/asynchronousexplicitimagecache-test.cpp +++ b/tests/unit/unittest/asynchronousexplicitimagecache-test.cpp @@ -74,7 +74,7 @@ TEST_F(AsynchronousExplicitImageCache, RequestImageCallsAbortCallbackWithoutEntr ON_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), _)) .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{})); - EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed))) + EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::NoEntry))) .WillRepeatedly([&](auto) { notification.notify(); }); cache.requestImage("/path/to/Component.qml", @@ -131,7 +131,7 @@ TEST_F(AsynchronousExplicitImageCache, RequestMidSizeImageCallsAbortCallbackWith ON_CALL(mockStorage, fetchMidSizeImage(Eq("/path/to/Component.qml"), _)) .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{})); - EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed))) + EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::NoEntry))) .WillRepeatedly([&](auto) { notification.notify(); }); cache.requestMidSizeImage("/path/to/Component.qml", @@ -188,7 +188,7 @@ TEST_F(AsynchronousExplicitImageCache, RequestSmallImageCallsAbortCallbackWithou ON_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml"), _)) .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{})); - EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed))) + EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::NoEntry))) .WillRepeatedly([&](auto) { notification.notify(); }); cache.requestSmallImage("/path/to/Component.qml",