feat: expire progress bar on popup notifs

This commit is contained in:
2026-11-09 21:39:21 +01:00
parent e49ecabf62
commit 37e0429563
5 changed files with 324 additions and 153 deletions
+39 -10
View File
@@ -134,7 +134,6 @@ class NotificationRow : public QObject {
friend class NotificationGroup;
friend NotificationStore;
[[nodiscard]] bool expired(qint64 now) const;
void refreshTimeStr();
QList<NotificationActionRow*> m_actions;
@@ -151,7 +150,7 @@ class NotificationRow : public QObject {
QString m_summary;
QString m_timeStr;
qint64 m_deadline = 0;
qint64 m_expireTimeoutMs = -1;
qint64 m_rowId = -1;
qint64 m_timeMs = 0;
@@ -159,15 +158,12 @@ class NotificationRow : public QObject {
QSet<QObject*> m_locks;
QJSValue m_jsNotification;
// The store has already dropped this row from its lists, but a delegate is
// still animating it out; the delete waits for the last unlock.
bool m_deleteAfterUnlock = false;
bool m_closed = false;
bool m_hasActionIcons = false;
bool m_liveConnected = false;
bool m_popup = false;
bool m_popupPaused = false;
bool m_resident = false;
int m_notificationId = 0;
@@ -186,6 +182,13 @@ class NotificationGroup : public QObject {
Q_PROPERTY(QString appIcon READ appIcon NOTIFY appIconChanged)
Q_PROPERTY(QString appName READ appName CONSTANT)
Q_PROPERTY(int count READ count NOTIFY countChanged)
Q_PROPERTY(bool expireActive READ expireActive NOTIFY expireActiveChanged)
Q_PROPERTY(bool expirePaused READ expirePaused NOTIFY expirePausedChanged)
Q_PROPERTY(
qreal expireProgress READ expireProgress NOTIFY expireProgressChanged)
Q_PROPERTY(
int expireRemaining READ expireRemaining NOTIFY expireRemainingChanged)
Q_PROPERTY(int expireTimeout READ expireTimeout NOTIFY expireTimeoutChanged)
Q_PROPERTY(QString firstSummary READ firstSummary NOTIFY firstSummaryChanged)
Q_PROPERTY(QString image READ image NOTIFY imageChanged)
Q_PROPERTY(
@@ -201,21 +204,34 @@ class NotificationGroup : public QObject {
[[nodiscard]] QString appIcon() const;
[[nodiscard]] QString appName() const;
[[nodiscard]] int count() const;
[[nodiscard]] bool expireActive() const;
[[nodiscard]] bool expirePaused() const;
[[nodiscard]] qreal expireProgress() const;
[[nodiscard]] int expireRemaining() const;
[[nodiscard]] int expireTimeout() const;
[[nodiscard]] QString firstSummary() const;
[[nodiscard]] QString image() const;
[[nodiscard]] QQmlListProperty<NotificationRow> notifs();
[[nodiscard]] QString timeStr() const;
[[nodiscard]] int urgency() const;
Q_INVOKABLE void holdExpiry();
Q_INVOKABLE bool isSameAppIcon(const QString& path) const;
Q_INVOKABLE bool isSameImage(const QString& path) const;
Q_INVOKABLE void lock(QObject* item);
Q_INVOKABLE void releaseExpiry();
Q_INVOKABLE void unlock(QObject* item);
signals:
void activeNotifsChanged();
void appIconChanged();
void countChanged();
void expireActiveChanged();
void expirePausedChanged();
void expireProgressChanged();
void expireRemainingChanged();
void expireTimerRestarted();
void expireTimeoutChanged();
void firstSummaryChanged();
void imageChanged();
void notifsChanged();
@@ -226,17 +242,19 @@ class NotificationGroup : public QObject {
friend NotificationStore;
void addRow(NotificationRow* row);
void clearExpiry();
bool expired(qint64 now) const;
void refreshActive();
void refreshExpiry(qint64 now);
void release();
void removeRow(NotificationRow* row);
void resetExpiry(int timeout);
void setExpireActive(bool active);
void sync();
QList<NotificationRow*> m_active;
QList<NotificationRow*> m_notifs;
// The group reads image/appIcon through the row it took them from, so a
// row swapping between its raw source and the cached blob keeps the card
// in sync instead of holding a stale copy.
QPointer<NotificationRow> m_iconSource;
QPointer<NotificationRow> m_imageSource;
QSet<QObject*> m_locks;
@@ -245,6 +263,15 @@ class NotificationGroup : public QObject {
QString m_firstSummary;
QString m_timeStr;
qint64 m_expireDeadline = 0;
qint64 m_expireHeld = 0;
qreal m_expireProgress = 0;
int m_expireRemaining = 0;
int m_expireTimeout = 0;
bool m_expireActive = false;
bool m_expirePaused = false;
int m_count = 0;
int m_urgency = 1;
bool m_releasing = false;
@@ -312,6 +339,8 @@ class NotificationStore : public QObject {
void applyNotifConfig(const config::Notifs* notifs);
void closeDb();
void copyFields(NotificationRow* row, QObject* notification);
[[nodiscard]] int expireTimeoutMs(const NotificationRow* row) const;
void expireGroup(NotificationGroup* group);
void flush();
void handleClosed(NotificationRow* row);
void insertDbRow(NotificationRow* row);
@@ -323,12 +352,12 @@ class NotificationStore : public QObject {
void prune();
void addPopupGroup(NotificationRow* row);
void removePopupGroupRow(NotificationRow* row);
void touchExpiry(NotificationRow* row);
[[nodiscard]] int groupExpireTimeoutMs(const NotificationGroup* group) const;
void updatePopupGroup(NotificationRow* row);
void registerProvider();
void removeGroupRow(NotificationRow* row);
void removeRow(NotificationRow* row);
// Coalesced rowsChanged: a burst of inserts/removes triggers one QML
// rebind on the next event loop turn instead of one per notification.
void scheduleRowsChanged();
[[nodiscard]] NotificationRow* rowForNotificationId(int id) const;
void startImageCache(NotificationRow* row);