Skip to content

Commit cdaff29

Browse files
committed
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.
1 parent c6791cf commit cdaff29

File tree

8 files changed

+75
-58
lines changed

8 files changed

+75
-58
lines changed

src/core/imageprovider.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
#include "imageprovider.hpp"
22

3+
#include <qcontainerfwd.h>
34
#include <qdebug.h>
45
#include <qimage.h>
56
#include <qlogging.h>
67
#include <qmap.h>
78
#include <qobject.h>
89
#include <qpixmap.h>
910
#include <qqmlengine.h>
11+
#include <qtypes.h>
1012

1113
namespace {
1214

15+
namespace {
1316
QMap<QString, QsImageHandle*> liveImages; // NOLINT
17+
quint32 handleIndex = 0; // NOLINT
18+
} // namespace
1419

1520
void parseReq(const QString& req, QString& target, QString& param) {
1621
auto splitIdx = req.indexOf('/');
@@ -24,14 +29,9 @@ void parseReq(const QString& req, QString& target, QString& param) {
2429

2530
} // namespace
2631

27-
QsImageHandle::QsImageHandle(QQmlImageProviderBase::ImageType type, QObject* parent)
28-
: QObject(parent)
29-
, type(type) {
30-
{
31-
auto dbg = QDebug(&this->id);
32-
dbg.nospace() << static_cast<void*>(this);
33-
}
34-
32+
QsImageHandle::QsImageHandle(QQmlImageProviderBase::ImageType type)
33+
: type(type)
34+
, id(QString::number(++handleIndex)) {
3535
liveImages.insert(this->id, this);
3636
}
3737

@@ -85,3 +85,9 @@ QsPixmapProvider::requestPixmap(const QString& id, QSize* size, const QSize& req
8585
return QPixmap();
8686
}
8787
}
88+
89+
QString QsIndexedImageHandle::url() const {
90+
return this->QsImageHandle::url() % '/' % QString::number(this->changeIndex);
91+
}
92+
93+
void QsIndexedImageHandle::imageChanged() { ++this->changeIndex; }

src/core/imageprovider.hpp

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,13 @@ class QsPixmapProvider: public QQuickImageProvider {
2020
QPixmap requestPixmap(const QString& id, QSize* size, const QSize& requestedSize) override;
2121
};
2222

23-
class QsImageHandle: public QObject {
24-
Q_OBJECT;
25-
23+
class QsImageHandle {
2624
public:
27-
explicit QsImageHandle(QQmlImageProviderBase::ImageType type, QObject* parent = nullptr);
28-
~QsImageHandle() override;
25+
explicit QsImageHandle(QQmlImageProviderBase::ImageType type);
26+
virtual ~QsImageHandle();
2927
Q_DISABLE_COPY_MOVE(QsImageHandle);
3028

31-
[[nodiscard]] QString url() const;
29+
[[nodiscard]] virtual QString url() const;
3230

3331
virtual QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize);
3432
virtual QPixmap requestPixmap(const QString& id, QSize* size, const QSize& requestedSize);
@@ -37,3 +35,14 @@ class QsImageHandle: public QObject {
3735
QQmlImageProviderBase::ImageType type;
3836
QString id;
3937
};
38+
39+
class QsIndexedImageHandle: public QsImageHandle {
40+
public:
41+
explicit QsIndexedImageHandle(QQmlImageProviderBase::ImageType type): QsImageHandle(type) {}
42+
43+
[[nodiscard]] QString url() const override;
44+
void imageChanged();
45+
46+
private:
47+
quint32 changeIndex = 0;
48+
};

src/dbus/dbusmenu/dbusmenu.cpp

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ QString DBusMenuItem::icon() const {
5959
this->iconName,
6060
this->menu->iconThemePath.value().join(':')
6161
);
62-
} else if (this->image != nullptr) {
63-
return this->image->url();
62+
} else if (this->image.hasData()) {
63+
return this->image.url();
6464
} else return nullptr;
6565
}
6666

@@ -113,7 +113,7 @@ void DBusMenuItem::updateProperties(const QVariantMap& properties, const QString
113113
auto originalEnabled = this->mEnabled;
114114
auto originalVisible = this->visible;
115115
auto originalIconName = this->iconName;
116-
auto* originalImage = this->image;
116+
auto imageChanged = false;
117117
auto originalIsSeparator = this->mSeparator;
118118
auto originalButtonType = this->mButtonType;
119119
auto originalToggleState = this->mCheckState;
@@ -173,12 +173,16 @@ void DBusMenuItem::updateProperties(const QVariantMap& properties, const QString
173173
if (iconData.canConvert<QByteArray>()) {
174174
auto data = iconData.value<QByteArray>();
175175
if (data.isEmpty()) {
176-
this->image = nullptr;
177-
} else if (this->image == nullptr || this->image->data != data) {
178-
this->image = new DBusMenuPngImage(data, this);
176+
imageChanged = this->image.hasData();
177+
this->image.data.clear();
178+
} else if (!this->image.hasData() || this->image.data != data) {
179+
imageChanged = true;
180+
this->image.data = data;
181+
this->image.imageChanged();
179182
}
180183
} else if (removed.isEmpty() || removed.contains("icon-data")) {
181-
this->image = nullptr;
184+
imageChanged = this->image.hasData();
185+
image.data.clear();
182186
}
183187

184188
auto type = properties.value("type");
@@ -239,17 +243,13 @@ void DBusMenuItem::updateProperties(const QVariantMap& properties, const QString
239243
if (this->mSeparator != originalIsSeparator) emit this->isSeparatorChanged();
240244
if (this->displayChildren != originalDisplayChildren) emit this->hasChildrenChanged();
241245

242-
if (this->iconName != originalIconName || this->image != originalImage) {
243-
if (this->image != originalImage) {
244-
delete originalImage;
245-
}
246-
246+
if (this->iconName != originalIconName || imageChanged) {
247247
emit this->iconChanged();
248248
}
249249

250250
qCDebug(logDbusMenu).nospace() << "Updated properties of " << this << " { label=" << this->mText
251251
<< ", enabled=" << this->mEnabled << ", visible=" << this->visible
252-
<< ", iconName=" << this->iconName << ", iconData=" << this->image
252+
<< ", iconName=" << this->iconName << ", iconData=" << &this->image
253253
<< ", separator=" << this->mSeparator
254254
<< ", toggleType=" << this->mButtonType
255255
<< ", toggleState=" << this->mCheckState

src/dbus/dbusmenu/dbusmenu.hpp

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,17 @@ namespace qs::dbus::dbusmenu {
3030
using menu::QsMenuEntry;
3131

3232
class DBusMenu;
33-
class DBusMenuPngImage;
33+
class DBusMenuItem;
34+
35+
class DBusMenuPngImage: public QsIndexedImageHandle {
36+
public:
37+
explicit DBusMenuPngImage(): QsIndexedImageHandle(QQuickImageProvider::Image) {}
38+
39+
[[nodiscard]] bool hasData() const { return !data.isEmpty(); }
40+
QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override;
41+
42+
QByteArray data;
43+
};
3444

3545
///! Menu item shared by an external program.
3646
/// Menu item shared by an external program via the
@@ -93,7 +103,7 @@ private slots:
93103
bool visible = true;
94104
bool mSeparator = false;
95105
QString iconName;
96-
DBusMenuPngImage* image = nullptr;
106+
DBusMenuPngImage image;
97107
menu::QsMenuButtonType::Enum mButtonType = menu::QsMenuButtonType::None;
98108
Qt::CheckState mCheckState = Qt::Unchecked;
99109
bool displayChildren = false;
@@ -156,17 +166,6 @@ private slots:
156166

157167
QDebug operator<<(QDebug debug, DBusMenu* menu);
158168

159-
class DBusMenuPngImage: public QsImageHandle {
160-
public:
161-
explicit DBusMenuPngImage(QByteArray data, DBusMenuItem* parent)
162-
: QsImageHandle(QQuickImageProvider::Image, parent)
163-
, data(std::move(data)) {}
164-
165-
QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override;
166-
167-
QByteArray data;
168-
};
169-
170169
class DBusMenuHandle;
171170

172171
QDebug operator<<(QDebug debug, const DBusMenuHandle* handle);

src/services/notifications/dbusimage.hpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
#pragma once
22

3-
#include <utility>
4-
53
#include <qdbusargument.h>
64
#include <qimage.h>
75
#include <qobject.h>
@@ -23,14 +21,22 @@ struct DBusNotificationImage {
2321
const QDBusArgument& operator>>(const QDBusArgument& argument, DBusNotificationImage& pixmap);
2422
const QDBusArgument& operator<<(QDBusArgument& argument, const DBusNotificationImage& pixmap);
2523

26-
class NotificationImage: public QsImageHandle {
24+
class NotificationImage: public QsIndexedImageHandle {
2725
public:
28-
explicit NotificationImage(DBusNotificationImage image, QObject* parent)
29-
: QsImageHandle(QQuickAsyncImageProvider::Image, parent)
30-
, image(std::move(image)) {}
26+
explicit NotificationImage(): QsIndexedImageHandle(QQuickAsyncImageProvider::Image) {}
27+
28+
[[nodiscard]] bool hasData() const { return !this->image.data.isEmpty(); }
29+
void clear() { this->image.data.clear(); }
30+
31+
[[nodiscard]] DBusNotificationImage& writeImage() {
32+
this->imageChanged();
33+
return this->image;
34+
}
3135

3236
QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override;
3337

38+
private:
3439
DBusNotificationImage image;
3540
};
41+
3642
} // namespace qs::service::notifications

src/services/notifications/notification.cpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include "notification.hpp"
2-
#include <utility>
32

43
#include <qcontainerfwd.h>
54
#include <qdbusargument.h>
@@ -117,21 +116,20 @@ void Notification::updateProperties(
117116

118117
QString imagePath;
119118

120-
if (!imageDataName.isEmpty()) {
119+
if (imageDataName.isEmpty()) {
120+
this->mImagePixmap.clear();
121+
} else {
121122
auto value = hints.value(imageDataName).value<QDBusArgument>();
122-
DBusNotificationImage image;
123-
value >> image;
124-
if (this->mImagePixmap) this->mImagePixmap->deleteLater();
125-
this->mImagePixmap = new NotificationImage(std::move(image), this);
126-
imagePath = this->mImagePixmap->url();
123+
value >> this->mImagePixmap.writeImage();
124+
imagePath = this->mImagePixmap.url();
127125
}
128126

129127
// don't store giant byte arrays longer than necessary
130128
hints.remove("image-data");
131129
hints.remove("image_data");
132130
hints.remove("icon_data");
133131

134-
if (!this->mImagePixmap) {
132+
if (!this->mImagePixmap.hasData()) {
135133
QString imagePathName;
136134
if (hints.contains("image-path")) imagePathName = "image-path";
137135
else if (hints.contains("image_path")) imagePathName = "image_path";

src/services/notifications/notification.hpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,10 @@
1212

1313
#include "../../core/retainable.hpp"
1414
#include "../../core/util.hpp"
15+
#include "dbusimage.hpp"
1516

1617
namespace qs::service::notifications {
1718

18-
class NotificationImage;
19-
2019
///! The urgency level of a Notification.
2120
/// See @@Notification.urgency.
2221
class NotificationUrgency: public QObject {
@@ -187,7 +186,7 @@ class Notification
187186
quint32 mId;
188187
NotificationCloseReason::Enum mCloseReason = NotificationCloseReason::Dismissed;
189188
bool mLastGeneration = false;
190-
NotificationImage* mImagePixmap = nullptr;
189+
NotificationImage mImagePixmap;
191190
QList<NotificationAction*> mActions;
192191

193192
// clang-format off

src/services/status_notifier/item.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ void StatusNotifierItem::onGetAllFailed() const {
282282
}
283283

284284
TrayImageHandle::TrayImageHandle(StatusNotifierItem* item)
285-
: QsImageHandle(QQmlImageProviderBase::Pixmap, item)
285+
: QsImageHandle(QQmlImageProviderBase::Pixmap)
286286
, item(item) {}
287287

288288
QPixmap

0 commit comments

Comments
 (0)