service/notifications: adopt bindable properties

This commit is contained in:
outfoxxed 2024-11-20 22:26:51 -08:00
parent abb900b7ff
commit a13c9d91b5
Signed by: outfoxxed
GPG key ID: 4C88A185FB89301E
2 changed files with 81 additions and 112 deletions

View file

@ -3,14 +3,16 @@
#include <qcontainerfwd.h> #include <qcontainerfwd.h>
#include <qdbusargument.h> #include <qdbusargument.h>
#include <qlist.h>
#include <qlogging.h> #include <qlogging.h>
#include <qloggingcategory.h> #include <qloggingcategory.h>
#include <qobject.h>
#include <qproperty.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
#include <qtypes.h> #include <qtypes.h>
#include "../../core/desktopentry.hpp" #include "../../core/desktopentry.hpp"
#include "../../core/iconimageprovider.hpp" #include "../../core/iconimageprovider.hpp"
#include "../../core/util.hpp"
#include "dbusimage.hpp" #include "dbusimage.hpp"
#include "server.hpp" #include "server.hpp"
@ -84,34 +86,51 @@ void Notification::updateProperties(
QVariantMap hints, QVariantMap hints,
qint32 expireTimeout qint32 expireTimeout
) { ) {
auto urgency = hints.contains("urgency") ? hints.value("urgency").value<quint8>() Qt::beginPropertyUpdateGroup();
: static_cast<quint8>(NotificationUrgency::Normal);
auto hasActionIcons = hints.value("action-icons").value<bool>(); this->bExpireTimeout = expireTimeout;
auto resident = hints.value("resident").value<bool>(); this->bAppName = appName;
auto transient = hints.value("transient").value<bool>(); this->bSummary = summary;
auto desktopEntry = hints.value("desktop-entry").value<QString>(); this->bBody = body;
this->bHasActionIcons = hints.value("action-icons").toBool();
this->bResident = hints.value("resident").toBool();
this->bTransient = hints.value("transient").toBool();
this->bDesktopEntry = hints.value("desktop-entry").toString();
this->bUrgency = hints.contains("urgency")
? hints.value("urgency").value<NotificationUrgency::Enum>()
: NotificationUrgency::Normal;
if (appIcon.isEmpty() && !this->bDesktopEntry.value().isEmpty()) {
if (auto* entry = DesktopEntryManager::instance()->byId(this->bDesktopEntry.value())) {
appIcon = entry->mIcon;
}
}
this->bAppIcon = appIcon;
QString imageDataName; QString imageDataName;
if (hints.contains("image-data")) imageDataName = "image-data"; if (hints.contains("image-data")) imageDataName = "image-data";
else if (hints.contains("image_data")) imageDataName = "image_data"; else if (hints.contains("image_data")) imageDataName = "image_data";
else if (hints.contains("icon_data")) imageDataName = "icon_data"; else if (hints.contains("icon_data")) imageDataName = "icon_data";
NotificationImage* imagePixmap = nullptr; QString imagePath;
if (!imageDataName.isEmpty()) { if (!imageDataName.isEmpty()) {
auto value = hints.value(imageDataName).value<QDBusArgument>(); auto value = hints.value(imageDataName).value<QDBusArgument>();
DBusNotificationImage image; DBusNotificationImage image;
value >> image; value >> image;
imagePixmap = new NotificationImage(std::move(image), this); if (this->mImagePixmap) this->mImagePixmap->deleteLater();
this->mImagePixmap = new NotificationImage(std::move(image), this);
imagePath = this->mImagePixmap->url();
} }
// don't store giant byte arrays more than necessary // don't store giant byte arrays longer than necessary
hints.remove("image-data"); hints.remove("image-data");
hints.remove("image_data"); hints.remove("image_data");
hints.remove("icon_data"); hints.remove("icon_data");
QString imagePath; if (!this->mImagePixmap) {
if (!imagePixmap) {
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";
@ -125,32 +144,10 @@ void Notification::updateProperties(
} }
} }
if (appIcon.isEmpty() && !desktopEntry.isEmpty()) { this->bImage = imagePath;
if (auto* entry = DesktopEntryManager::instance()->byId(desktopEntry)) { this->bHints = hints;
appIcon = entry->mIcon;
}
}
auto expireTimeoutChanged = this->setExpireTimeout(expireTimeout); Qt::endPropertyUpdateGroup();
auto appNameChanged = this->setAppName(appName);
auto appIconChanged = this->setAppIcon(appIcon);
auto summaryChanged = this->setSummary(summary);
auto bodyChanged = this->setBody(body);
auto urgencyChanged = this->setUrgency(static_cast<NotificationUrgency::Enum>(urgency));
auto hasActionIconsChanged = this->setHasActionIcons(hasActionIcons);
auto residentChanged = this->setResident(resident);
auto transientChanged = this->setTransient(transient);
auto desktopEntryChanged = this->setDesktopEntry(desktopEntry);
DEFINE_DROP_EMIT_IF(imagePixmap || imagePath != this->mImagePath, this, imageChanged);
auto hintsChanged = this->setHints(hints);
NotificationImage* oldImage = nullptr;
if (imageChanged) {
oldImage = this->mImagePixmap;
this->mImagePixmap = imagePixmap;
this->mImagePath = imagePath;
}
bool actionsChanged = false; bool actionsChanged = false;
auto deletedActions = QVector<NotificationAction*>(); auto deletedActions = QVector<NotificationAction*>();
@ -191,32 +188,16 @@ void Notification::updateProperties(
<< "sent an action set of an invalid length."; << "sent an action set of an invalid length.";
} }
DropEmitter::call(
expireTimeoutChanged,
appNameChanged,
appIconChanged,
summaryChanged,
bodyChanged,
urgencyChanged,
hasActionIconsChanged,
residentChanged,
transientChanged,
desktopEntryChanged,
imageChanged,
hintsChanged
);
if (actionsChanged) emit this->actionsChanged(); if (actionsChanged) emit this->actionsChanged();
for (auto* action: deletedActions) { for (auto* action: deletedActions) {
delete action; delete action;
} }
delete oldImage;
} }
quint32 Notification::id() const { return this->mId; } quint32 Notification::id() const { return this->mId; }
bool Notification::isTracked() const { return this->mCloseReason == 0; } bool Notification::isTracked() const { return this->mCloseReason == 0; }
QList<NotificationAction*> Notification::actions() const { return this->mActions; }
NotificationCloseReason::Enum Notification::closeReason() const { return this->mCloseReason; } NotificationCloseReason::Enum Notification::closeReason() const { return this->mCloseReason; }
void Notification::setTracked(bool tracked) { void Notification::setTracked(bool tracked) {
@ -228,26 +209,4 @@ void Notification::setTracked(bool tracked) {
bool Notification::isLastGeneration() const { return this->mLastGeneration; } bool Notification::isLastGeneration() const { return this->mLastGeneration; }
void Notification::setLastGeneration() { this->mLastGeneration = true; } void Notification::setLastGeneration() { this->mLastGeneration = true; }
DEFINE_MEMBER_GETSET(Notification, expireTimeout, setExpireTimeout);
DEFINE_MEMBER_GETSET(Notification, appName, setAppName);
DEFINE_MEMBER_GETSET(Notification, appIcon, setAppIcon);
DEFINE_MEMBER_GETSET(Notification, summary, setSummary);
DEFINE_MEMBER_GETSET(Notification, body, setBody);
DEFINE_MEMBER_GETSET(Notification, urgency, setUrgency);
DEFINE_MEMBER_GET(Notification, actions);
DEFINE_MEMBER_GETSET(Notification, hasActionIcons, setHasActionIcons);
DEFINE_MEMBER_GETSET(Notification, resident, setResident);
DEFINE_MEMBER_GETSET(Notification, transient, setTransient);
DEFINE_MEMBER_GETSET(Notification, desktopEntry, setDesktopEntry);
QString Notification::image() const {
if (this->mImagePixmap) {
return this->mImagePixmap->url();
} else {
return this->mImagePath;
}
}
DEFINE_MEMBER_GETSET(Notification, hints, setHints);
} // namespace qs::service::notifications } // namespace qs::service::notifications

View file

@ -3,6 +3,7 @@
#include <utility> #include <utility>
#include <qcontainerfwd.h> #include <qcontainerfwd.h>
#include <qlist.h>
#include <qmap.h> #include <qmap.h>
#include <qobject.h> #include <qobject.h>
#include <qqmlintegration.h> #include <qqmlintegration.h>
@ -80,18 +81,18 @@ class Notification
/// if @@NotificationServer.keepOnReload is true. /// if @@NotificationServer.keepOnReload is true.
Q_PROPERTY(bool lastGeneration READ isLastGeneration CONSTANT); Q_PROPERTY(bool lastGeneration READ isLastGeneration CONSTANT);
/// Time in seconds the notification should be valid for /// Time in seconds the notification should be valid for
Q_PROPERTY(qreal expireTimeout READ expireTimeout NOTIFY expireTimeoutChanged); Q_PROPERTY(qreal expireTimeout READ expireTimeout NOTIFY expireTimeoutChanged BINDABLE bindableExpireTimeout);
/// The sending application's name. /// The sending application's name.
Q_PROPERTY(QString appName READ appName NOTIFY appNameChanged); Q_PROPERTY(QString appName READ appName NOTIFY appNameChanged BINDABLE bindableAppName);
/// The sending application's icon. If none was provided, then the icon from an associated /// The sending application's icon. If none was provided, then the icon from an associated
/// desktop entry will be retrieved. If none was found then "". /// desktop entry will be retrieved. If none was found then "".
Q_PROPERTY(QString appIcon READ appIcon NOTIFY appIconChanged); Q_PROPERTY(QString appIcon READ appIcon NOTIFY appIconChanged BINDABLE bindableAppIcon);
/// The image associated with this notification, or "" if none. /// The image associated with this notification, or "" if none.
Q_PROPERTY(QString summary READ summary NOTIFY summaryChanged); Q_PROPERTY(QString summary READ summary NOTIFY summaryChanged BINDABLE bindableSummary);
Q_PROPERTY(QString body READ body NOTIFY bodyChanged); Q_PROPERTY(QString body READ body NOTIFY bodyChanged BINDABLE bindableBody);
Q_PROPERTY(qs::service::notifications::NotificationUrgency::Enum urgency READ urgency NOTIFY urgencyChanged); Q_PROPERTY(qs::service::notifications::NotificationUrgency::Enum urgency READ urgency NOTIFY urgencyChanged BINDABLE bindableUrgency);
/// Actions that can be taken for this notification. /// Actions that can be taken for this notification.
Q_PROPERTY(QVector<qs::service::notifications::NotificationAction*> actions READ actions NOTIFY actionsChanged); Q_PROPERTY(QList<qs::service::notifications::NotificationAction*> actions READ actions NOTIFY actionsChanged);
/// If actions associated with this notification have icons available. /// If actions associated with this notification have icons available.
/// ///
/// See @@NotificationAction.identifier for details. /// See @@NotificationAction.identifier for details.
@ -136,15 +137,29 @@ public:
void close(NotificationCloseReason::Enum reason); void close(NotificationCloseReason::Enum reason);
[[nodiscard]] quint32 id() const; [[nodiscard]] quint32 id() const;
[[nodiscard]] bool isTracked() const; [[nodiscard]] bool isTracked() const;
[[nodiscard]] NotificationCloseReason::Enum closeReason() const;
void setTracked(bool tracked);
[[nodiscard]] bool isLastGeneration() const; [[nodiscard]] bool isLastGeneration() const;
void setLastGeneration(); void setLastGeneration();
[[nodiscard]] QString image() const; QS_BINDABLE_GETTER(qreal, bExpireTimeout, expireTimeout, bindableExpireTimeout);
QS_BINDABLE_GETTER(QString, bAppName, appName, bindableAppName);
QS_BINDABLE_GETTER(QString, bAppIcon, appIcon, bindableAppIcon);
QS_BINDABLE_GETTER(QString, bSummary, summary, bindableSummary);
QS_BINDABLE_GETTER(QString, bBody, body, bindableBody);
QS_BINDABLE_GETTER(NotificationUrgency::Enum, bUrgency, urgency, bindableUrgency);
[[nodiscard]] QList<NotificationAction*> actions() const;
QS_BINDABLE_GETTER(bool, bHasActionIcons, hasActionIcons, bindableHasActionIcons);
QS_BINDABLE_GETTER(bool, bResident, resident, bindableResident);
QS_BINDABLE_GETTER(bool, bTransient, transient, bindableTransient);
QS_BINDABLE_GETTER(QString, bDesktopEntry, desktopEntry, bindableDesktopEntry);
QS_BINDABLE_GETTER(QString, bImage, image, bindableImage);
QS_BINDABLE_GETTER(QVariantMap, bHints, hints, bindableHints);
[[nodiscard]] NotificationCloseReason::Enum closeReason() const;
void setTracked(bool tracked);
signals: signals:
/// Sent when a notification has been closed. /// Sent when a notification has been closed.
@ -171,35 +186,30 @@ private:
quint32 mId; quint32 mId;
NotificationCloseReason::Enum mCloseReason = NotificationCloseReason::Dismissed; NotificationCloseReason::Enum mCloseReason = NotificationCloseReason::Dismissed;
bool mLastGeneration = false; bool mLastGeneration = false;
qreal mExpireTimeout = 0;
QString mAppName;
QString mAppIcon;
QString mSummary;
QString mBody;
NotificationUrgency::Enum mUrgency = NotificationUrgency::Normal;
QVector<NotificationAction*> mActions;
bool mHasActionIcons = false;
bool mResident = false;
bool mTransient = false;
QString mImagePath;
NotificationImage* mImagePixmap = nullptr; NotificationImage* mImagePixmap = nullptr;
QString mDesktopEntry; QList<NotificationAction*> mActions;
QVariantMap mHints;
// clang-format off // clang-format off
DECLARE_PRIVATE_MEMBER(Notification, expireTimeout, setExpireTimeout, mExpireTimeout, expireTimeoutChanged); Q_OBJECT_BINDABLE_PROPERTY(Notification, qreal, bExpireTimeout, &Notification::expireTimeoutChanged);
DECLARE_PRIVATE_MEMBER(Notification, appName, setAppName, mAppName, appNameChanged); Q_OBJECT_BINDABLE_PROPERTY(Notification, QString, bAppName, &Notification::appNameChanged);
DECLARE_PRIVATE_MEMBER(Notification, appIcon, setAppIcon, mAppIcon, appIconChanged); Q_OBJECT_BINDABLE_PROPERTY(Notification, QString, bAppIcon, &Notification::appIconChanged);
DECLARE_PRIVATE_MEMBER(Notification, summary, setSummary, mSummary, summaryChanged); Q_OBJECT_BINDABLE_PROPERTY(Notification, QString, bSummary, &Notification::summaryChanged);
DECLARE_PRIVATE_MEMBER(Notification, body, setBody, mBody, bodyChanged); Q_OBJECT_BINDABLE_PROPERTY(Notification, QString, bBody, &Notification::bodyChanged);
DECLARE_PRIVATE_MEMBER(Notification, urgency, setUrgency, mUrgency, urgencyChanged); Q_OBJECT_BINDABLE_PROPERTY(Notification, bool, bHasActionIcons, &Notification::hasActionIconsChanged);
DECLARE_MEMBER_WITH_GET(Notification, actions, mActions, actionsChanged); Q_OBJECT_BINDABLE_PROPERTY(Notification, bool, bResident, &Notification::residentChanged);
DECLARE_PRIVATE_MEMBER(Notification, hasActionIcons, setHasActionIcons, mHasActionIcons, hasActionIconsChanged); Q_OBJECT_BINDABLE_PROPERTY(Notification, bool, bTransient, &Notification::transientChanged);
DECLARE_PRIVATE_MEMBER(Notification, resident, setResident, mResident, residentChanged); Q_OBJECT_BINDABLE_PROPERTY(Notification, QString, bDesktopEntry, &Notification::desktopEntryChanged);
DECLARE_PRIVATE_MEMBER(Notification, transient, setTransient, mTransient, transientChanged); Q_OBJECT_BINDABLE_PROPERTY(Notification, QString, bImage, &Notification::imageChanged);
DECLARE_PRIVATE_MEMBER(Notification, desktopEntry, setDesktopEntry, mDesktopEntry, desktopEntryChanged); Q_OBJECT_BINDABLE_PROPERTY(Notification, QVariantMap, bHints, &Notification::hintsChanged);
DECLARE_PRIVATE_MEMBER(Notification, hints, setHints, mHints, hintsChanged);
// clang-format on // clang-format on
Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(
Notification,
NotificationUrgency::Enum,
bUrgency,
NotificationUrgency::Normal,
&Notification::urgencyChanged
);
}; };
///! An action associated with a Notification. ///! An action associated with a Notification.