Verified Commit 8b641963 authored by Fushan Wen's avatar Fushan Wen 💬
Browse files

wallpapers/image: move image part to `MediaProxy` and update `ImageBackendTest`

This simplifies ImageBackend, and makes the config dialog load slightly faster.
parent 80539089
Pipeline #190284 passed with stage
in 6 minutes and 22 seconds
......@@ -16,7 +16,7 @@ import org.kde.plasma.core 2.0 as PlasmaCore
QQC2.StackView {
id: root
readonly property url modelImage: imageWallpaper.modelImage
readonly property url modelImage: mediaProxy.modelImage
readonly property int fillMode: wallpaper.configuration.FillMode
readonly property string configColor: wallpaper.configuration.Color
readonly property bool blur: wallpaper.configuration.Blur
......@@ -47,7 +47,7 @@ QQC2.StackView {
// e.g. used by slideshow wallpaper plugin
function action_open() {
imageWallpaper.openModelImage();
mediaProxy.openModelImage();
}
//private
......@@ -68,9 +68,20 @@ QQC2.StackView {
usedInConfig: false
//the oneliner of difference between image and slideshow wallpapers
renderingMode: (wallpaper.pluginName === "org.kde.image") ? Wallpaper.ImageBackend.SingleImage : Wallpaper.ImageBackend.SlideShow
image: {
if (wallpaper.pluginName !== "org.kde.image") {
return "";
targetSize: root.sourceSize
slidePaths: wallpaper.configuration.SlidePaths
slideTimer: wallpaper.configuration.SlideInterval
slideshowMode: wallpaper.configuration.SlideshowMode
slideshowFoldersFirst: wallpaper.configuration.SlideshowFoldersFirst
uncheckedSlides: wallpaper.configuration.UncheckedSlides
}
Wallpaper.MediaProxy {
id: mediaProxy
source: {
if (wallpaper.pluginName === "org.kde.slideshow") {
return imageWallpaper.image;
}
if (wallpaper.configuration.PreviewImage !== "null") {
return wallpaper.configuration.PreviewImage;
......@@ -78,11 +89,6 @@ QQC2.StackView {
return wallpaper.configuration.Image;
}
targetSize: root.sourceSize
slidePaths: wallpaper.configuration.SlidePaths
slideTimer: wallpaper.configuration.SlideInterval
slideshowMode: wallpaper.configuration.SlideshowMode
slideshowFoldersFirst: wallpaper.configuration.SlideshowFoldersFirst
uncheckedSlides: wallpaper.configuration.UncheckedSlides
onColorSchemeChanged: Qt.callLater(loadImage);
}
......@@ -110,7 +116,7 @@ QQC2.StackView {
wallpaper.loading = false;
if (pendingImage.status !== Image.Ready) {
imageWallpaper.useSingleImageDefaults();
mediaProxy.useSingleImageDefaults();
}
}
}
......
......@@ -15,6 +15,8 @@ set(image_SRCS
model/imagelistmodel.cpp
model/imageproxymodel.cpp
provider/packageimageprovider.cpp
provider/providertype.h
utils/mediaproxy.cpp
)
ecm_qt_declare_logging_category(image_SRCS HEADER debug.h
......
......@@ -27,9 +27,15 @@ TestCase {
targetSize: Qt.size(root.width, root.height)
}
Wallpaper.MediaProxy {
id: mediaProxy
source: imageWallpaper.image
targetSize: imageWallpaper.targetSize
}
SignalSpy {
id: modelImageChangedSignalSpy
target: imageWallpaper
target: mediaProxy
signalName: "modelImageChanged"
}
......@@ -61,7 +67,7 @@ TestCase {
width: root.width
height: root.height
visible: true
title: imageWallpaper.modelImage.toString()
title: mediaProxy.modelImage.toString()
}
function test_setSingleImage() {
......@@ -69,9 +75,9 @@ TestCase {
verify(testImage.toString().length > 0);
imageWallpaper.image = testImage;
compare(imageWallpaper.modelImage, testImage);
compare(mediaProxy.modelImage, testImage);
const image = createTemporaryObject(mainImage, window, {source: imageWallpaper.modelImage});
const image = createTemporaryObject(mainImage, window, {source: mediaProxy.modelImage});
compare(image.status, Image.Ready);
const grabbed = grabImage(image);
compare(grabbed.pixel(0, 0), Qt.rgba(0, 0, 0, 255));
......@@ -82,9 +88,9 @@ TestCase {
verify(testPackage.toString().length > 0);
imageWallpaper.image = testPackage;
compare(imageWallpaper.modelImage.toString().indexOf("image://package/get?dir="), 0);
compare(mediaProxy.modelImage.toString().indexOf("image://package/get?dir="), 0);
const image = createTemporaryObject(mainImage, window, {source: imageWallpaper.modelImage});
const image = createTemporaryObject(mainImage, window, {source: mediaProxy.modelImage});
compare(image.status, Image.Loading);
image.wait();
compare(image.status, Image.Ready);
......@@ -117,13 +123,13 @@ TestCase {
imageWallpaper.renderingMode = Wallpaper.ImageBackend.SlideShow;
wait(1000); // &SlideModel::done
let image = imageWallpaper.modelImage;
let image = mediaProxy.modelImage;
imageWallpaper.nextSlide();
verify(image != imageWallpaper.modelImage);
verify(image != mediaProxy.modelImage);
image = imageWallpaper.modelImage;
image = mediaProxy.modelImage;
imageWallpaper.nextSlide();
verify(image != imageWallpaper.modelImage);
verify(image != mediaProxy.modelImage);
}
}
......@@ -19,16 +19,13 @@
#include <QImageReader>
#include <QMimeDatabase>
#include <QScreen>
#include <QUrlQuery>
#include <QStandardPaths>
#include <KConfigGroup>
#include <KIO/CopyJob>
#include <KIO/Job>
#include <KIO/OpenUrlJob>
#include <KLocalizedString>
#include <KNotificationJobUiDelegate>
#include <KPackage/PackageLoader>
#include <Plasma/Theme>
#include "debug.h"
#include "finder/packagefinder.h"
......@@ -40,11 +37,8 @@ ImageBackend::ImageBackend(QObject *parent)
: QObject(parent)
, m_targetSize(qGuiApp->primaryScreen()->size() * qGuiApp->primaryScreen()->devicePixelRatio())
, m_slideFilterModel(new SlideFilterModel(this))
, m_isDarkColorScheme(isDarkColorScheme())
{
connect(&m_timer, &QTimer::timeout, this, &ImageBackend::nextSlide);
useSingleImageDefaults();
}
ImageBackend::~ImageBackend()
......@@ -58,17 +52,10 @@ void ImageBackend::classBegin()
void ImageBackend::componentComplete()
{
// don't bother loading single image until all properties have settled
// otherwise we would load a too small image (initial view size) just
// to load the proper one afterwards etc etc
m_ready = true;
// Follow system color scheme
connect(qGuiApp, &QGuiApplication::paletteChanged, this, &ImageBackend::slotSystemPaletteChanged);
if (m_mode == SingleImage) {
setSingleImage();
} else if (m_mode == SlideShow) {
// MediaProxy will handle SingleImage case
if (m_mode == SlideShow) {
startSlideshow();
}
}
......@@ -80,19 +67,12 @@ QString ImageBackend::image() const
void ImageBackend::setImage(const QString &url)
{
if (m_image.toString() == url || url.isEmpty()) {
if (url.isEmpty() || m_image == QUrl(url)) {
return;
}
m_image = QUrl(url);
Q_EMIT imageChanged();
setSingleImage();
}
QUrl ImageBackend::modelImage() const
{
return m_modelImage;
}
ImageBackend::RenderingMode ImageBackend::renderingMode() const
......@@ -110,9 +90,6 @@ void ImageBackend::setRenderingMode(RenderingMode mode)
if (m_mode == SlideShow) {
startSlideshow();
} else {
// we need to reset the preferred image
setSingleImage();
}
}
......@@ -167,69 +144,10 @@ void ImageBackend::setTargetSize(const QSize &size)
m_targetSize = size;
if (m_ready && m_providerType == Provider::Package) {
Q_EMIT modelImageChanged();
}
// Will relay to ImageProxyModel
Q_EMIT targetSizeChanged(m_targetSize);
}
void ImageBackend::useSingleImageDefaults()
{
m_image.clear();
// Try from the look and feel package first, then from the plasma theme
KPackage::Package lookAndFeelPackage = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel"));
KConfigGroup cg(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), "KDE");
const QString packageName = cg.readEntry("LookAndFeelPackage", QString());
// If empty, it will be the default (currently Breeze)
if (!packageName.isEmpty()) {
lookAndFeelPackage.setPath(packageName);
}
KConfigGroup lnfDefaultsConfig = KConfigGroup(KSharedConfig::openConfig(lookAndFeelPackage.filePath("defaults")), "Wallpaper");
const QString image = lnfDefaultsConfig.readEntry("Image", "");
KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Wallpaper/Images"));
if (!image.isEmpty()) {
package.setPath(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("wallpapers/") + image, QStandardPaths::LocateDirectory));
if (package.isValid()) {
m_image = QUrl::fromLocalFile(package.path());
}
}
// Try to get a default from the plasma theme
if (m_image.isEmpty()) {
Plasma::Theme theme;
QString path = theme.wallpaperPath();
int index = path.indexOf(QLatin1String("/contents/images/"));
if (index > -1) { // We have file from package -> get path to package
m_image = QUrl::fromLocalFile(path.left(index));
} else {
m_image = QUrl::fromLocalFile(path);
}
package.setPath(m_image.toLocalFile());
if (!package.isValid()) {
return;
}
}
PackageFinder::findPreferredImageInPackage(package, m_targetSize);
// Make sure the image can be read, or there will be dead loops.
if (m_image.isEmpty() || QImage(package.filePath("preferred")).isNull()) {
return;
}
Q_EMIT imageChanged();
setSingleImage();
}
QAbstractItemModel *ImageBackend::wallpaperModel()
{
if (!m_model) {
......@@ -254,15 +172,6 @@ SlideModel *ImageBackend::slideshowModel()
return m_slideshowModel;
}
bool ImageBackend::isDarkColorScheme(const QPalette &palette) const noexcept
{
// 192 is from kcm_colors
if (palette == QPalette()) {
return qGray(qGuiApp->palette().window().color().rgb()) < 192;
}
return qGray(palette.window().color().rgb()) < 192;
}
QAbstractItemModel *ImageBackend::slideFilterModel()
{
if (!m_slideFilterModel->sourceModel()) {
......@@ -384,69 +293,6 @@ void ImageBackend::addDirFromSelectionDialog()
}
}
void ImageBackend::setSingleImage()
{
if (!m_ready || m_image.isEmpty()) {
return;
}
// supposedly QSize::isEmpty() is true if "either width or height are >= 0"
if (!m_targetSize.width() || !m_targetSize.height()) {
return;
}
if (m_image.isLocalFile()) {
const QFileInfo info(m_image.toLocalFile());
if (!info.exists()) {
return;
}
if (info.isFile()) {
m_providerType = Provider::Image;
} else {
m_providerType = Provider::Package;
}
} else {
// The url can be without file://, try again.
const QFileInfo info(m_image.toString());
if (!info.exists()) {
return;
}
if (info.isFile()) {
m_providerType = Provider::Image;
} else {
m_providerType = Provider::Package;
}
m_image = QUrl::fromLocalFile(info.filePath());
}
switch (m_providerType) {
case Provider::Image:
m_modelImage = m_image;
break;
case Provider::Package: {
// Use a custom image provider
QUrl url(QStringLiteral("image://package/get"));
QUrlQuery urlQuery(url);
urlQuery.addQueryItem(QStringLiteral("dir"), m_image.toLocalFile());
url.setQuery(urlQuery);
m_modelImage = url;
break;
}
}
if (!m_modelImage.isEmpty()) {
Q_EMIT modelImageChanged();
}
}
void ImageBackend::startSlideshow()
{
if (!m_ready || m_usedInConfig || m_mode != SlideShow) {
......@@ -473,33 +319,11 @@ void ImageBackend::backgroundsFound()
}
// start slideshow
if (m_currentSlide == -1) {
m_currentSlide = m_slideFilterModel->indexOf(m_image.toString()) - 1;
} else {
m_currentSlide = -1;
}
m_currentSlide = -1;
m_slideFilterModel->sort(0);
nextSlide();
}
void ImageBackend::slotSystemPaletteChanged(const QPalette &palette)
{
if (m_providerType != Provider::Package || m_usedInConfig) {
// Currently only KPackage supports adaptive wallpapers
return;
}
const bool dark = isDarkColorScheme(palette);
if (dark == m_isDarkColorScheme) {
return;
}
m_isDarkColorScheme = dark;
Q_EMIT colorSchemeChanged();
}
void ImageBackend::showFileDialog()
{
if (!m_dialog) {
......@@ -603,11 +427,10 @@ void ImageBackend::nextSlide()
m_timer.stop();
m_timer.start(m_delay * 1000);
if (next.isEmpty()) {
m_image = QUrl(previousPath); // setSingleImage will add "file://"
m_image = QUrl(previousPath);
} else {
m_image = QUrl(next);
Q_EMIT imageChanged();
setSingleImage();
}
}
......@@ -640,43 +463,6 @@ void ImageBackend::openFolder(const QString &path)
job->start();
}
void ImageBackend::openModelImage() const
{
QUrl url;
switch (m_providerType) {
case Provider::Image: {
url = m_image;
break;
}
case Provider::Package: {
KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Wallpaper/Images"));
package.setPath(m_image.toLocalFile());
if (!package.isValid()) {
return;
}
PackageFinder::findPreferredImageInPackage(package, m_targetSize);
url = QUrl::fromLocalFile(package.filePath("preferred"));
if (isDarkColorScheme()) {
const QUrl darkUrl = package.fileUrl("preferredDark");
if (!darkUrl.isEmpty()) {
url = darkUrl;
}
}
break;
}
}
KIO::OpenUrlJob *job = new KIO::OpenUrlJob(url);
job->setUiDelegate(new KNotificationJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled));
job->start();
}
QStringList ImageBackend::uncheckedSlides() const
{
return m_uncheckedSlides;
......
......@@ -11,7 +11,6 @@
#pragma once
#include <QAbstractItemModel>
#include <QPalette>
#include <QQmlParserStatus>
#include <QSize>
#include <QTimer>
......@@ -38,17 +37,12 @@ class ImageBackend : public QObject, public QQmlParserStatus, public SortingMode
Q_PROPERTY(RenderingMode renderingMode READ renderingMode WRITE setRenderingMode NOTIFY renderingModeChanged)
Q_PROPERTY(SortingMode::Mode slideshowMode READ slideshowMode WRITE setSlideshowMode NOTIFY slideshowModeChanged)
Q_PROPERTY(bool slideshowFoldersFirst READ slideshowFoldersFirst WRITE setSlideshowFoldersFirst NOTIFY slideshowFoldersFirstChanged)
/**
* Package path from the saved configuration, can be an image file, a url with
* "image://" scheme or a folder (KPackage).
* Provides source url for \MediaProxy
*/
Q_PROPERTY(QString image READ image WRITE setImage NOTIFY imageChanged)
/**
* The real path of the image
* e.g. /home/kde/Pictures/image.png
* image://package/get? (KPackage)
*/
Q_PROPERTY(QUrl modelImage READ modelImage NOTIFY modelImageChanged)
Q_PROPERTY(QAbstractItemModel *wallpaperModel READ wallpaperModel CONSTANT)
Q_PROPERTY(QAbstractItemModel *slideFilterModel READ slideFilterModel CONSTANT)
Q_PROPERTY(int slideTimer READ slideTimer WRITE setSlideTimer NOTIFY slideTimerChanged)
......@@ -68,32 +62,21 @@ public:
};
Q_ENUM(RenderingMode)
enum class Provider {
Image,
Package,
};
Q_ENUM(Provider)
explicit ImageBackend(QObject *parent = nullptr);
~ImageBackend() override;
QString image() const;
void setImage(const QString &url);
QUrl modelImage() const;
// this is for QML use
Q_INVOKABLE void addSlidePath(const QUrl &url);
Q_INVOKABLE void removeSlidePath(const QString &path);
Q_INVOKABLE void openFolder(const QString &path);
Q_INVOKABLE void openModelImage() const;
Q_INVOKABLE void showFileDialog();
Q_INVOKABLE QString addUsersWallpaper(const QUrl &url);
Q_INVOKABLE void useSingleImageDefaults();
RenderingMode renderingMode() const;
void setRenderingMode(RenderingMode mode);
......@@ -130,16 +113,12 @@ public Q_SLOTS:
Q_SIGNALS:
void settingsChanged();
void imageChanged();
void modelImageChanged();
void renderingModeChanged();
void slideshowModeChanged();
void slideshowFoldersFirstChanged();
void targetSizeChanged(const QSize &size);
void slideTimerChanged();
void usersWallpapersChanged();
void slidePathsChanged();
void resizeMethodChanged();
void customWallpaperPicked(const QString &path);
void uncheckedSlidesChanged();
void loadingChanged();
......@@ -148,12 +127,6 @@ Q_SIGNALS:
*/
void wallpaperBrowseCompleted();
/**
* Emitted when system color scheme changes. The frontend is required to
* reload the wallpaper even if the image path is not changed.
*/
void colorSchemeChanged();
protected Q_SLOTS:
void showAddSlidePathsDialog();
void slotWallpaperBrowseCompleted();
......@@ -161,32 +134,17 @@ protected Q_SLOTS:
void addDirFromSelectionDialog();
void backgroundsFound();
/**
* Switches to dark-colored wallpaper if available when system color
* scheme is dark.
*
* @since 5.26
*/
void slotSystemPaletteChanged(const QPalette &palette);
protected:
void setSingleImage();
private:
SlideModel *slideshowModel();
inline bool isDarkColorScheme(const QPalette &palette = {}) const noexcept;
bool m_ready = false;
int m_delay = 10;
QUrl m_image;
QUrl m_modelImage;
QSize m_targetSize;
bool m_usedInConfig = true;
RenderingMode m_mode = SingleImage;
Provider m_providerType = Provider::Image;
SortingMode::Mode m_slideshowMode = SortingMode::Random;
bool m_slideshowFoldersFirst = false;
......@@ -199,6 +157,4 @@ private:
SlideModel *m_slideshowModel = nullptr;
SlideFilterModel *m_slideFilterModel;
QFileDialog *m_dialog = nullptr;
bool m_isDarkColorScheme;
};
......@@ -12,6 +12,7 @@
#include "imagebackend.h"
#include "provider/packageimageprovider.h"
#include "sortingmode.h"
#include "utils/mediaproxy.h"
const auto pluginName = QByteArrayLiteral("org.kde.plasma.wallpapers.image");
......@@ -29,6 +30,8 @@ void ImagePlugin::registerTypes(const char *uri)
qRegisterMetaType<KFileItem>(); // For image preview
qmlRegisterType<ImageBackend>(uri, 2, 0, "ImageBackend");
qmlRegisterType<MediaProxy>(uri, 2, 0, "MediaProxy");
qmlRegisterAnonymousType<QAbstractItemModel>("QAbstractItemModel", 1);
qmlRegisterUncreatableType<SortingMode>(uri, 2, 0, "SortingMode", QStringLiteral("error: only enums"));
}