From 7c8ca948dae769731f02f06c2f813692ca4d5681 Mon Sep 17 00:00:00 2001 From: zhangkun Date: Sat, 14 Mar 2026 14:33:48 +0800 Subject: [PATCH] feat: add hover protection for notification bubbles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added bubble ID tracking to prevent premature closure when hovering over notifications. The BubbleModel now exposes bubbleId role for QML access. When a bubble is hovered, its ID is passed to the notification server which blocks timeout-based closure for that specific bubble. This prevents notifications from disappearing while users are interacting with them. Log: Notification bubbles now remain visible when hovered over, preventing accidental closure feat: 为通知气泡添加悬停保护功能 在 BubbleModel 中添加了 bubbleId 角色供 QML 访问。当气泡被悬停时,其 ID 会传递给通知服务器,服务器会阻止该特定气泡的超时关闭。这防止了用户与通知 交互时通知意外消失的问题。 Log: 通知气泡在悬停时保持可见,防止意外关闭 PMS: BUG-352577 --- panels/notification/bubble/bubblepanel.cpp | 4 ++++ panels/notification/bubble/bubblepanel.h | 3 ++- panels/notification/bubble/package/Bubble.qml | 4 ++-- .../server/notificationmanager.cpp | 23 ++++++++++++++++++- .../notification/server/notificationmanager.h | 4 +++- .../server/notifyserverapplet.cpp | 7 +++++- .../notification/server/notifyserverapplet.h | 3 ++- 7 files changed, 41 insertions(+), 7 deletions(-) diff --git a/panels/notification/bubble/bubblepanel.cpp b/panels/notification/bubble/bubblepanel.cpp index 8c0edc912..df180214b 100644 --- a/panels/notification/bubble/bubblepanel.cpp +++ b/panels/notification/bubble/bubblepanel.cpp @@ -206,6 +206,10 @@ void BubblePanel::setEnabled(bool newEnabled) setVisible(!isEmpty && enabled()); } +void BubblePanel::setHoveredId(qint64 id) +{ + QMetaObject::invokeMethod(m_notificationServer, "setBlockClosedId", Qt::DirectConnection, Q_ARG(qint64, id)); +} } #include "bubblepanel.moc" diff --git a/panels/notification/bubble/bubblepanel.h b/panels/notification/bubble/bubblepanel.h index 5aa84a528..c6770220f 100644 --- a/panels/notification/bubble/bubblepanel.h +++ b/panels/notification/bubble/bubblepanel.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -39,6 +39,7 @@ public Q_SLOTS: void close(int bubbleIndex, int reason); void delayProcess(int bubbleIndex); void setEnabled(bool newEnabled); + void setHoveredId(qint64 id); Q_SIGNALS: void visibleChanged(); diff --git a/panels/notification/bubble/package/Bubble.qml b/panels/notification/bubble/package/Bubble.qml index 10f3e3875..78762df9a 100644 --- a/panels/notification/bubble/package/Bubble.qml +++ b/panels/notification/bubble/package/Bubble.qml @@ -14,9 +14,9 @@ Control { property var bubble onHoveredChanged: function () { if (control.hovered) { - Applet.bubbles.delayRemovedBubble = bubble.id + Applet.setHoveredId(control.bubble.id) } else { - Applet.bubbles.delayRemovedBubble = 0 + Applet.setHoveredId(0) } } diff --git a/panels/notification/server/notificationmanager.cpp b/panels/notification/server/notificationmanager.cpp index ab2b9d313..ae7e31b40 100644 --- a/panels/notification/server/notificationmanager.cpp +++ b/panels/notification/server/notificationmanager.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -347,6 +347,21 @@ QVariant NotificationManager::GetSystemInfo(uint configItem) return m_setting->systemValue(static_cast(configItem)); } +void NotificationManager::setBlockClosedId(qint64 id) +{ + auto findIter = std::find_if(m_pendingTimeoutEntities.begin(), m_pendingTimeoutEntities.end(), [this](const NotifyEntity &entity) { + return entity.id() == m_blockClosedId; + }); + const auto current = QDateTime::currentMSecsSinceEpoch(); + if(m_blockClosedId != NotifyEntity::InvalidId && findIter != m_pendingTimeoutEntities.end() && current > findIter.key()) { + qDebug(notifyLog) << "Block close bubble id:" << m_blockClosedId << "for the new block bubble id:" << id; + m_pendingTimeoutEntities.insert(QDateTime::currentMSecsSinceEpoch() + 1000, findIter.value()); + m_pendingTimeoutEntities.erase(findIter); + } + m_blockClosedId = id; + onHandingPendingEntities(); +} + bool NotificationManager::isDoNotDisturb() const { if (!m_setting->systemValue(NotificationSetting::DNDMode).toBool()) @@ -673,6 +688,12 @@ void NotificationManager::onHandingPendingEntities() continue; } + if (item.id() == m_blockClosedId) { + qDebug(notifyLog) << "bubble id:" << item.bubbleId() << "entity id:" << item.id(); + m_pendingTimeoutEntities.insert(current + 1000, item); + continue; + } + qDebug(notifyLog) << "Expired for the notification " << item.id() << item.appName(); notificationClosed(item.id(), item.bubbleId(), NotifyEntity::Expired); } diff --git a/panels/notification/server/notificationmanager.h b/panels/notification/server/notificationmanager.h index 2bd838e87..790ab7f59 100644 --- a/panels/notification/server/notificationmanager.h +++ b/panels/notification/server/notificationmanager.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -65,6 +65,7 @@ public Q_SLOTS: void SetSystemInfo(uint configItem, const QVariant &value); QVariant GetSystemInfo(uint configItem); + void setBlockClosedId(qint64 id); private: bool isDoNotDisturb() const; bool recordNotification(NotifyEntity &entity); @@ -97,6 +98,7 @@ private slots: QStringList m_systemApps; QMap m_appNamesMap; int m_cleanupDays = 7; + qint64 m_blockClosedId = 0; }; } // notification diff --git a/panels/notification/server/notifyserverapplet.cpp b/panels/notification/server/notifyserverapplet.cpp index fb81829b5..b4de43dfc 100644 --- a/panels/notification/server/notifyserverapplet.cpp +++ b/panels/notification/server/notifyserverapplet.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -104,6 +104,11 @@ void NotifyServerApplet::removeExpiredNotifications() m_manager->removeExpiredNotifications(); } +void NotifyServerApplet::setBlockClosedId(qint64 id) +{ + m_manager->setBlockClosedId(id); +} + D_APPLET_CLASS(NotifyServerApplet) } diff --git a/panels/notification/server/notifyserverapplet.h b/panels/notification/server/notifyserverapplet.h index 9704b3231..20975e91d 100644 --- a/panels/notification/server/notifyserverapplet.h +++ b/panels/notification/server/notifyserverapplet.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -31,6 +31,7 @@ public Q_SLOTS: void removeNotifications(const QString &appName); void removeNotifications(); void removeExpiredNotifications(); + void setBlockClosedId(qint64 id); private: NotificationManager *m_manager = nullptr;