forked from quickshell/quickshell
core/icon: stop reusing image ids (dbusmenu, notifications)
Fixes issues caused by the QML engine caching old pixmaps using the same IDs as new ones, notably dbusmenu icons.
This commit is contained in:
parent
c6791cf1f2
commit
cdaff2967f
8 changed files with 75 additions and 58 deletions
|
@ -1,5 +1,6 @@
|
||||||
#include "imageprovider.hpp"
|
#include "imageprovider.hpp"
|
||||||
|
|
||||||
|
#include <qcontainerfwd.h>
|
||||||
#include <qdebug.h>
|
#include <qdebug.h>
|
||||||
#include <qimage.h>
|
#include <qimage.h>
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
|
@ -7,10 +8,14 @@
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
#include <qpixmap.h>
|
#include <qpixmap.h>
|
||||||
#include <qqmlengine.h>
|
#include <qqmlengine.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
namespace {
|
||||||
QMap<QString, QsImageHandle*> liveImages; // NOLINT
|
QMap<QString, QsImageHandle*> liveImages; // NOLINT
|
||||||
|
quint32 handleIndex = 0; // NOLINT
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void parseReq(const QString& req, QString& target, QString& param) {
|
void parseReq(const QString& req, QString& target, QString& param) {
|
||||||
auto splitIdx = req.indexOf('/');
|
auto splitIdx = req.indexOf('/');
|
||||||
|
@ -24,14 +29,9 @@ void parseReq(const QString& req, QString& target, QString& param) {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
QsImageHandle::QsImageHandle(QQmlImageProviderBase::ImageType type, QObject* parent)
|
QsImageHandle::QsImageHandle(QQmlImageProviderBase::ImageType type)
|
||||||
: QObject(parent)
|
: type(type)
|
||||||
, type(type) {
|
, id(QString::number(++handleIndex)) {
|
||||||
{
|
|
||||||
auto dbg = QDebug(&this->id);
|
|
||||||
dbg.nospace() << static_cast<void*>(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
liveImages.insert(this->id, this);
|
liveImages.insert(this->id, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,3 +85,9 @@ QsPixmapProvider::requestPixmap(const QString& id, QSize* size, const QSize& req
|
||||||
return QPixmap();
|
return QPixmap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString QsIndexedImageHandle::url() const {
|
||||||
|
return this->QsImageHandle::url() % '/' % QString::number(this->changeIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QsIndexedImageHandle::imageChanged() { ++this->changeIndex; }
|
||||||
|
|
|
@ -20,15 +20,13 @@ public:
|
||||||
QPixmap requestPixmap(const QString& id, QSize* size, const QSize& requestedSize) override;
|
QPixmap requestPixmap(const QString& id, QSize* size, const QSize& requestedSize) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class QsImageHandle: public QObject {
|
class QsImageHandle {
|
||||||
Q_OBJECT;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit QsImageHandle(QQmlImageProviderBase::ImageType type, QObject* parent = nullptr);
|
explicit QsImageHandle(QQmlImageProviderBase::ImageType type);
|
||||||
~QsImageHandle() override;
|
virtual ~QsImageHandle();
|
||||||
Q_DISABLE_COPY_MOVE(QsImageHandle);
|
Q_DISABLE_COPY_MOVE(QsImageHandle);
|
||||||
|
|
||||||
[[nodiscard]] QString url() const;
|
[[nodiscard]] virtual QString url() const;
|
||||||
|
|
||||||
virtual QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize);
|
virtual QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize);
|
||||||
virtual QPixmap requestPixmap(const QString& id, QSize* size, const QSize& requestedSize);
|
virtual QPixmap requestPixmap(const QString& id, QSize* size, const QSize& requestedSize);
|
||||||
|
@ -37,3 +35,14 @@ private:
|
||||||
QQmlImageProviderBase::ImageType type;
|
QQmlImageProviderBase::ImageType type;
|
||||||
QString id;
|
QString id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class QsIndexedImageHandle: public QsImageHandle {
|
||||||
|
public:
|
||||||
|
explicit QsIndexedImageHandle(QQmlImageProviderBase::ImageType type): QsImageHandle(type) {}
|
||||||
|
|
||||||
|
[[nodiscard]] QString url() const override;
|
||||||
|
void imageChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
quint32 changeIndex = 0;
|
||||||
|
};
|
||||||
|
|
|
@ -59,8 +59,8 @@ QString DBusMenuItem::icon() const {
|
||||||
this->iconName,
|
this->iconName,
|
||||||
this->menu->iconThemePath.value().join(':')
|
this->menu->iconThemePath.value().join(':')
|
||||||
);
|
);
|
||||||
} else if (this->image != nullptr) {
|
} else if (this->image.hasData()) {
|
||||||
return this->image->url();
|
return this->image.url();
|
||||||
} else return nullptr;
|
} else return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ void DBusMenuItem::updateProperties(const QVariantMap& properties, const QString
|
||||||
auto originalEnabled = this->mEnabled;
|
auto originalEnabled = this->mEnabled;
|
||||||
auto originalVisible = this->visible;
|
auto originalVisible = this->visible;
|
||||||
auto originalIconName = this->iconName;
|
auto originalIconName = this->iconName;
|
||||||
auto* originalImage = this->image;
|
auto imageChanged = false;
|
||||||
auto originalIsSeparator = this->mSeparator;
|
auto originalIsSeparator = this->mSeparator;
|
||||||
auto originalButtonType = this->mButtonType;
|
auto originalButtonType = this->mButtonType;
|
||||||
auto originalToggleState = this->mCheckState;
|
auto originalToggleState = this->mCheckState;
|
||||||
|
@ -173,12 +173,16 @@ void DBusMenuItem::updateProperties(const QVariantMap& properties, const QString
|
||||||
if (iconData.canConvert<QByteArray>()) {
|
if (iconData.canConvert<QByteArray>()) {
|
||||||
auto data = iconData.value<QByteArray>();
|
auto data = iconData.value<QByteArray>();
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
this->image = nullptr;
|
imageChanged = this->image.hasData();
|
||||||
} else if (this->image == nullptr || this->image->data != data) {
|
this->image.data.clear();
|
||||||
this->image = new DBusMenuPngImage(data, this);
|
} else if (!this->image.hasData() || this->image.data != data) {
|
||||||
|
imageChanged = true;
|
||||||
|
this->image.data = data;
|
||||||
|
this->image.imageChanged();
|
||||||
}
|
}
|
||||||
} else if (removed.isEmpty() || removed.contains("icon-data")) {
|
} else if (removed.isEmpty() || removed.contains("icon-data")) {
|
||||||
this->image = nullptr;
|
imageChanged = this->image.hasData();
|
||||||
|
image.data.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto type = properties.value("type");
|
auto type = properties.value("type");
|
||||||
|
@ -239,17 +243,13 @@ void DBusMenuItem::updateProperties(const QVariantMap& properties, const QString
|
||||||
if (this->mSeparator != originalIsSeparator) emit this->isSeparatorChanged();
|
if (this->mSeparator != originalIsSeparator) emit this->isSeparatorChanged();
|
||||||
if (this->displayChildren != originalDisplayChildren) emit this->hasChildrenChanged();
|
if (this->displayChildren != originalDisplayChildren) emit this->hasChildrenChanged();
|
||||||
|
|
||||||
if (this->iconName != originalIconName || this->image != originalImage) {
|
if (this->iconName != originalIconName || imageChanged) {
|
||||||
if (this->image != originalImage) {
|
|
||||||
delete originalImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit this->iconChanged();
|
emit this->iconChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
qCDebug(logDbusMenu).nospace() << "Updated properties of " << this << " { label=" << this->mText
|
qCDebug(logDbusMenu).nospace() << "Updated properties of " << this << " { label=" << this->mText
|
||||||
<< ", enabled=" << this->mEnabled << ", visible=" << this->visible
|
<< ", enabled=" << this->mEnabled << ", visible=" << this->visible
|
||||||
<< ", iconName=" << this->iconName << ", iconData=" << this->image
|
<< ", iconName=" << this->iconName << ", iconData=" << &this->image
|
||||||
<< ", separator=" << this->mSeparator
|
<< ", separator=" << this->mSeparator
|
||||||
<< ", toggleType=" << this->mButtonType
|
<< ", toggleType=" << this->mButtonType
|
||||||
<< ", toggleState=" << this->mCheckState
|
<< ", toggleState=" << this->mCheckState
|
||||||
|
|
|
@ -30,7 +30,17 @@ namespace qs::dbus::dbusmenu {
|
||||||
using menu::QsMenuEntry;
|
using menu::QsMenuEntry;
|
||||||
|
|
||||||
class DBusMenu;
|
class DBusMenu;
|
||||||
class DBusMenuPngImage;
|
class DBusMenuItem;
|
||||||
|
|
||||||
|
class DBusMenuPngImage: public QsIndexedImageHandle {
|
||||||
|
public:
|
||||||
|
explicit DBusMenuPngImage(): QsIndexedImageHandle(QQuickImageProvider::Image) {}
|
||||||
|
|
||||||
|
[[nodiscard]] bool hasData() const { return !data.isEmpty(); }
|
||||||
|
QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override;
|
||||||
|
|
||||||
|
QByteArray data;
|
||||||
|
};
|
||||||
|
|
||||||
///! Menu item shared by an external program.
|
///! Menu item shared by an external program.
|
||||||
/// Menu item shared by an external program via the
|
/// Menu item shared by an external program via the
|
||||||
|
@ -93,7 +103,7 @@ private:
|
||||||
bool visible = true;
|
bool visible = true;
|
||||||
bool mSeparator = false;
|
bool mSeparator = false;
|
||||||
QString iconName;
|
QString iconName;
|
||||||
DBusMenuPngImage* image = nullptr;
|
DBusMenuPngImage image;
|
||||||
menu::QsMenuButtonType::Enum mButtonType = menu::QsMenuButtonType::None;
|
menu::QsMenuButtonType::Enum mButtonType = menu::QsMenuButtonType::None;
|
||||||
Qt::CheckState mCheckState = Qt::Unchecked;
|
Qt::CheckState mCheckState = Qt::Unchecked;
|
||||||
bool displayChildren = false;
|
bool displayChildren = false;
|
||||||
|
@ -156,17 +166,6 @@ private:
|
||||||
|
|
||||||
QDebug operator<<(QDebug debug, DBusMenu* menu);
|
QDebug operator<<(QDebug debug, DBusMenu* menu);
|
||||||
|
|
||||||
class DBusMenuPngImage: public QsImageHandle {
|
|
||||||
public:
|
|
||||||
explicit DBusMenuPngImage(QByteArray data, DBusMenuItem* parent)
|
|
||||||
: QsImageHandle(QQuickImageProvider::Image, parent)
|
|
||||||
, data(std::move(data)) {}
|
|
||||||
|
|
||||||
QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override;
|
|
||||||
|
|
||||||
QByteArray data;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DBusMenuHandle;
|
class DBusMenuHandle;
|
||||||
|
|
||||||
QDebug operator<<(QDebug debug, const DBusMenuHandle* handle);
|
QDebug operator<<(QDebug debug, const DBusMenuHandle* handle);
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include <qdbusargument.h>
|
#include <qdbusargument.h>
|
||||||
#include <qimage.h>
|
#include <qimage.h>
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
|
@ -23,14 +21,22 @@ struct DBusNotificationImage {
|
||||||
const QDBusArgument& operator>>(const QDBusArgument& argument, DBusNotificationImage& pixmap);
|
const QDBusArgument& operator>>(const QDBusArgument& argument, DBusNotificationImage& pixmap);
|
||||||
const QDBusArgument& operator<<(QDBusArgument& argument, const DBusNotificationImage& pixmap);
|
const QDBusArgument& operator<<(QDBusArgument& argument, const DBusNotificationImage& pixmap);
|
||||||
|
|
||||||
class NotificationImage: public QsImageHandle {
|
class NotificationImage: public QsIndexedImageHandle {
|
||||||
public:
|
public:
|
||||||
explicit NotificationImage(DBusNotificationImage image, QObject* parent)
|
explicit NotificationImage(): QsIndexedImageHandle(QQuickAsyncImageProvider::Image) {}
|
||||||
: QsImageHandle(QQuickAsyncImageProvider::Image, parent)
|
|
||||||
, image(std::move(image)) {}
|
[[nodiscard]] bool hasData() const { return !this->image.data.isEmpty(); }
|
||||||
|
void clear() { this->image.data.clear(); }
|
||||||
|
|
||||||
|
[[nodiscard]] DBusNotificationImage& writeImage() {
|
||||||
|
this->imageChanged();
|
||||||
|
return this->image;
|
||||||
|
}
|
||||||
|
|
||||||
QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override;
|
QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override;
|
||||||
|
|
||||||
|
private:
|
||||||
DBusNotificationImage image;
|
DBusNotificationImage image;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace qs::service::notifications
|
} // namespace qs::service::notifications
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#include "notification.hpp"
|
#include "notification.hpp"
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include <qcontainerfwd.h>
|
#include <qcontainerfwd.h>
|
||||||
#include <qdbusargument.h>
|
#include <qdbusargument.h>
|
||||||
|
@ -117,13 +116,12 @@ void Notification::updateProperties(
|
||||||
|
|
||||||
QString imagePath;
|
QString imagePath;
|
||||||
|
|
||||||
if (!imageDataName.isEmpty()) {
|
if (imageDataName.isEmpty()) {
|
||||||
|
this->mImagePixmap.clear();
|
||||||
|
} else {
|
||||||
auto value = hints.value(imageDataName).value<QDBusArgument>();
|
auto value = hints.value(imageDataName).value<QDBusArgument>();
|
||||||
DBusNotificationImage image;
|
value >> this->mImagePixmap.writeImage();
|
||||||
value >> image;
|
imagePath = this->mImagePixmap.url();
|
||||||
if (this->mImagePixmap) this->mImagePixmap->deleteLater();
|
|
||||||
this->mImagePixmap = new NotificationImage(std::move(image), this);
|
|
||||||
imagePath = this->mImagePixmap->url();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't store giant byte arrays longer than necessary
|
// don't store giant byte arrays longer than necessary
|
||||||
|
@ -131,7 +129,7 @@ void Notification::updateProperties(
|
||||||
hints.remove("image_data");
|
hints.remove("image_data");
|
||||||
hints.remove("icon_data");
|
hints.remove("icon_data");
|
||||||
|
|
||||||
if (!this->mImagePixmap) {
|
if (!this->mImagePixmap.hasData()) {
|
||||||
QString imagePathName;
|
QString imagePathName;
|
||||||
if (hints.contains("image-path")) imagePathName = "image-path";
|
if (hints.contains("image-path")) imagePathName = "image-path";
|
||||||
else if (hints.contains("image_path")) imagePathName = "image_path";
|
else if (hints.contains("image_path")) imagePathName = "image_path";
|
||||||
|
|
|
@ -12,11 +12,10 @@
|
||||||
|
|
||||||
#include "../../core/retainable.hpp"
|
#include "../../core/retainable.hpp"
|
||||||
#include "../../core/util.hpp"
|
#include "../../core/util.hpp"
|
||||||
|
#include "dbusimage.hpp"
|
||||||
|
|
||||||
namespace qs::service::notifications {
|
namespace qs::service::notifications {
|
||||||
|
|
||||||
class NotificationImage;
|
|
||||||
|
|
||||||
///! The urgency level of a Notification.
|
///! The urgency level of a Notification.
|
||||||
/// See @@Notification.urgency.
|
/// See @@Notification.urgency.
|
||||||
class NotificationUrgency: public QObject {
|
class NotificationUrgency: public QObject {
|
||||||
|
@ -187,7 +186,7 @@ private:
|
||||||
quint32 mId;
|
quint32 mId;
|
||||||
NotificationCloseReason::Enum mCloseReason = NotificationCloseReason::Dismissed;
|
NotificationCloseReason::Enum mCloseReason = NotificationCloseReason::Dismissed;
|
||||||
bool mLastGeneration = false;
|
bool mLastGeneration = false;
|
||||||
NotificationImage* mImagePixmap = nullptr;
|
NotificationImage mImagePixmap;
|
||||||
QList<NotificationAction*> mActions;
|
QList<NotificationAction*> mActions;
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
|
|
@ -282,7 +282,7 @@ void StatusNotifierItem::onGetAllFailed() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
TrayImageHandle::TrayImageHandle(StatusNotifierItem* item)
|
TrayImageHandle::TrayImageHandle(StatusNotifierItem* item)
|
||||||
: QsImageHandle(QQmlImageProviderBase::Pixmap, item)
|
: QsImageHandle(QQmlImageProviderBase::Pixmap)
|
||||||
, item(item) {}
|
, item(item) {}
|
||||||
|
|
||||||
QPixmap
|
QPixmap
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue