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

This simplifies ImageBackend, and makes the config dialog load slightly faster.
wilder-5.26
Fushan Wen 4 years ago
parent 80539089c9
commit 8b64196342
No known key found for this signature in database
GPG Key ID: 2E48D1487C91DCAA
  1. 28
      wallpapers/image/imagepackage/contents/ui/main.qml
  2. 2
      wallpapers/image/plugin/CMakeLists.txt
  3. 26
      wallpapers/image/plugin/autotests/tst_imagebackend.qml
  4. 226
      wallpapers/image/plugin/imagebackend.cpp
  5. 50
      wallpapers/image/plugin/imagebackend.h
  6. 3
      wallpapers/image/plugin/imageplugin.cpp
  7. 20
      wallpapers/image/plugin/provider/providertype.h
  8. 288
      wallpapers/image/plugin/utils/mediaproxy.cpp
  9. 102
      wallpapers/image/plugin/utils/mediaproxy.h

@ -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"));
}

@ -0,0 +1,20 @@
/*
SPDX-FileCopyrightText: 2022 Fushan Wen <qydwhotmail@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
class Provider
{
Q_GADGET
public:
enum class Type {
Unknown,
Image,
Package,
};
Q_ENUM(Provider)
};

@ -0,0 +1,288 @@
/*
SPDX-FileCopyrightText: 2022 Fushan Wen <qydwhotmail@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "mediaproxy.h"
#include <QFileInfo>
#include <QGuiApplication>
#include <QScreen>
#include <QUrlQuery>
#include <KConfigGroup>
#include <KIO/OpenUrlJob>
#include <KNotificationJobUiDelegate>
#include <KPackage/PackageLoader>
#include <Plasma/Theme>
#include "../finder/packagefinder.h"
#include "debug.h"
MediaProxy::MediaProxy(QObject *parent)
: QObject(parent)
, m_targetSize(qGuiApp->primaryScreen()->size() * qGuiApp->primaryScreen()->devicePixelRatio())
, m_isDarkColorScheme(isDarkColorScheme())
{
useSingleImageDefaults();
}
void MediaProxy::classBegin()
{
}
void MediaProxy::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, &MediaProxy::slotSystemPaletteChanged);
updateModelImage();
}
QString MediaProxy::source() const
{
return m_source.toString();
}
void MediaProxy::setSource(const QString &url)
{
if (m_source.toString() == url) {
return;
}
m_source = QUrl(url);
Q_EMIT sourceChanged();
m_providerType = determineType(m_source);
updateModelImage();
}
QUrl MediaProxy::modelImage() const
{
return m_modelImage;
}
QSize MediaProxy::targetSize() const
{
return m_targetSize;
}
void MediaProxy::setTargetSize(const QSize &size)
{
if (m_targetSize == size) {
return;
}
m_targetSize = size;
Q_EMIT targetSizeChanged(size);
if (m_providerType == Provider::Type::Package) {
updateModelImage();
}
}
Provider::Type MediaProxy::providerType() const
{
return m_providerType;
}
void MediaProxy::openModelImage()
{
QUrl url;
switch (m_providerType) {
case Provider::Type::Image: {
url = m_modelImage;
break;
}
case Provider::Type::Package: {
url = findPreferredImageInPackage();
break;
}
default:
return;
}
KIO::OpenUrlJob *job = new KIO::OpenUrlJob(url);
job->setUiDelegate(new KNotificationJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled));
job->start();
}
void MediaProxy::useSingleImageDefaults()
{
// 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_source = QUrl::fromLocalFile(package.path());
}
}
// Try to get a default from the plasma theme
if (m_source.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_source = QUrl::fromLocalFile(path.left(index));
} else {
m_source = QUrl::fromLocalFile(path);
}
package.setPath(m_source.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_source.isEmpty() || QImage(package.filePath("preferred")).isNull()) {
return;
}
m_providerType = determineType(m_source);
updateModelImage();
}
QUrl MediaProxy::formatUrl(const QUrl &url)
{
if (url.isLocalFile()) {
return url;
}
// The url can be without file://, try again.
return QUrl::fromLocalFile(url.toString());
}
void MediaProxy::slotSystemPaletteChanged(const QPalette &palette)
{
if (m_providerType != Provider::Type::Package) {
// Currently only KPackage supports adaptive wallpapers
return;
}
const bool dark = isDarkColorScheme(palette);
if (dark == m_isDarkColorScheme) {
return;
}
m_isDarkColorScheme = dark;
Q_EMIT colorSchemeChanged();
}
bool MediaProxy::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;
}
Provider::Type MediaProxy::determineType(const QUrl &url)
{
QFileInfo info(formatUrl(url).toLocalFile());
if (info.isFile()) {
return Provider::Type::Image;
} else if (info.isDir()) {
return Provider::Type::Package;
}
return Provider::Type::Unknown;
}
QUrl MediaProxy::findPreferredImageInPackage()
{
KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Wallpaper/Images"));
package.setPath(m_source.toLocalFile());
QUrl url;
if (!package.isValid()) {
return url;
}
PackageFinder::findPreferredImageInPackage(package, m_targetSize);
url = QUrl::fromLocalFile(package.filePath("preferred"));
if (isDarkColorScheme()) {
const QUrl darkUrl = package.fileUrl("preferredDark");
if (!darkUrl.isEmpty()) {
url = darkUrl;
}
}
return url;
}
void MediaProxy::updateModelImage()
{
if (!m_ready) {
return;
}
QUrl newRealSource;
switch (m_providerType) {
case Provider::Type::Image: {
newRealSource = formatUrl(m_source);
break;
}
case Provider::Type::Package: {
// Use a custom image provider
QUrl composedUrl(QStringLiteral("image://package/get"));
QUrlQuery urlQuery(composedUrl);
urlQuery.addQueryItem(QStringLiteral("dir"), formatUrl(m_source).toLocalFile());
// To make modelImageChaged work
urlQuery.addQueryItem(QStringLiteral("targetWidth"), QString::number(m_targetSize.width()));
urlQuery.addQueryItem(QStringLiteral("targetHeight"), QString::number(m_targetSize.height()));
composedUrl.setQuery(urlQuery);
newRealSource = composedUrl;
break;
}
case Provider::Type::Unknown:
default:
return;
}
if (m_modelImage == newRealSource) {
return;
}
m_modelImage = newRealSource;
Q_EMIT modelImageChanged();
}

@ -0,0 +1,102 @@
/*
SPDX-FileCopyrightText: 2022 Fushan Wen <qydwhotmail@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef MEDIAPROXY_H
#define MEDIAPROXY_H
#include <QObject>
#include <QPalette>
#include <QQmlParserStatus>
#include <QSize>
#include <QUrl>
#include "../provider/providertype.h"
/**
* A proxy class that converts a provider url to a real resource url.
*/
class MediaProxy : public QObject, public QQmlParserStatus, public Provider
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
/**
* Package path from the saved configuration, can be an image file, a url with
* "image://" scheme or a folder (KPackage).
*/
Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged)
/**
* 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(QSize targetSize READ targetSize WRITE setTargetSize NOTIFY targetSizeChanged)
public:
explicit MediaProxy(QObject *parent = nullptr);
void classBegin() override;
void componentComplete() override;
QString source() const;
void setSource(const QString &url);
QUrl modelImage() const;
QSize targetSize() const;
void setTargetSize(const QSize &size);
Provider::Type providerType() const;
Q_INVOKABLE void openModelImage();
Q_INVOKABLE void useSingleImageDefaults();
static QUrl formatUrl(const QUrl &url);
Q_SIGNALS:
void sourceChanged();
void modelImageChanged();
void targetSizeChanged(const QSize &size);
/**
* Emitted when system color scheme changes. The frontend is required to
* reload the wallpaper even if the image path is not changed.
*/
void colorSchemeChanged();
private Q_SLOTS:
/**
* Switches to dark-colored wallpaper if available when system color
* scheme is dark.
*
* @since 5.26
*/
void slotSystemPaletteChanged(const QPalette &palette);
private:
inline bool isDarkColorScheme(const QPalette &palette = {}) const noexcept;
Provider::Type determineType(const QUrl &url);
QUrl findPreferredImageInPackage();
void updateModelImage();
bool m_ready = false;
QUrl m_source;
QUrl m_modelImage;
Provider::Type m_providerType = Provider::Type::Unknown;
QSize m_targetSize;
bool m_isDarkColorScheme;
};
#endif // MEDIAPROXY_H
Loading…
Cancel
Save