From 15b58ed71b77f1b5d0e00822d641b05702b56db8 Mon Sep 17 00:00:00 2001 From: chenyuanbo Date: Tue, 7 Jul 2026 10:31:00 +0800 Subject: [PATCH] fix: optimize bubble replacement logic with silent replace support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Refactored BubbleModel API: replaced `isReplaceBubble()` and `replaceBubble(BubbleItem*)` with cleaner `replaceBubbleIndex(NotifyEntity)`, `updateBubbleInPlace()`, and `replaceBubble(int, BubbleItem*)` methods 2. Added support for "silent replace" via `x-deepin-SilentReplace` hint: when two bubbles match and the hint is true, the existing bubble is updated in-place using `dataChanged` rather than full remove+insert 3. Changed `replaceBubbleIndex` to accept `NotifyEntity` instead of `BubbleItem*`, enabling replacement logic before creating a new `BubbleItem` 4. `replaceBubble` now handles resource cleanup by calling `deleteLater()` on both old and new bubbles as needed 5. Removed the no longer needed `isReplaceBubble()` method, simplifying the public interface Log: Refactored bubble replacement mechanism; added silent in-place update for performance Influence: 1. Test notification replacement with and without `x-deepin- SilentReplace` hint 2. Verify that in-place update works for silent replacements (no visual artifacts) 3. Test replacement with different app names and bubble IDs to confirm no false matches 4. Verify memory management: old bubbles are properly deleted via `deleteLater()` 5. Test boundary cases: invalid replaceIndex, null bubble, concurrent notifications 6. Verify that `dataChanged` signal correctly triggers view updates for non-silent replacements refactor: 优化气泡替换逻辑,支持静默替换 1. 重构 BubbleModel API:将 `isReplaceBubble()` 和 `replaceBubble(BubbleItem*)` 替换为更清晰的 `replaceBubbleIndex(NotifyEntity)`、`updateBubbleInPlace()` 和 `replaceBubble(int, BubbleItem*)` 2. 通过 `x-deepin-SilentReplace` 提示增加"静默替换"支持:当气泡匹配且提 示为 true 时,使用 `dataChanged` 原地更新现有气泡,避免完整的移除-插入 操作 3. 将 `replaceBubbleIndex` 改为接受 `NotifyEntity` 而非 `BubbleItem*`, 允许在创建 `BubbleItem` 之前执行替换逻辑 4. `replaceBubble` 现在通过调用新旧气泡的 `deleteLater()` 来处理资源清理 5. 移除不再需要的 `isReplaceBubble()` 方法,简化公开接口 Log: 重构气泡替换机制;增加静默原地更新以提升性能 Influence: 1. 测试带或不带 `x-deepin-SilentReplace` 提示的通知替换 2. 验证静默替换时原地更新正常工作(无视觉异常) 3. 测试不同应用名和气泡 ID 的替换,确认无错误匹配 4. 验证内存管理:旧气泡通过 `deleteLater()` 正确释放 5. 测试边界情况:无效的 replaceIndex、空指针、并发通知 6. 验证对于非静默替换,`dataChanged` 信号正确触发视图更新 PMS: BUG-367205 --- panels/notification/bubble/bubblemodel.cpp | 66 ++++++++++++++-------- panels/notification/bubble/bubblemodel.h | 6 +- panels/notification/bubble/bubblepanel.cpp | 18 +++--- 3 files changed, 58 insertions(+), 32 deletions(-) diff --git a/panels/notification/bubble/bubblemodel.cpp b/panels/notification/bubble/bubblemodel.cpp index 44382c4fe..567e5a141 100644 --- a/panels/notification/bubble/bubblemodel.cpp +++ b/panels/notification/bubble/bubblemodel.cpp @@ -84,15 +84,51 @@ void BubbleModel::insertBubble(BubbleItem *bubble) endInsertRows(); } -bool BubbleModel::isReplaceBubble(const BubbleItem *bubble) const +int BubbleModel::replaceBubbleIndex(const NotifyEntity &entity) const { - return replaceBubbleIndex(bubble) >= 0; + if (entity.isReplace()) { + for (int i = 0; i < m_bubbles.size(); i++) { + const auto item = m_bubbles[i]; + if (!item) + continue; + + if (item->appName() != entity.appName()) + continue; + + if (item->bubbleId() == entity.bubbleId()) { + return i; + } + } + } + return -1; +} + +void BubbleModel::updateBubbleInPlace(int replaceIndex, const NotifyEntity &entity) +{ + if (replaceIndex < 0 || replaceIndex >= m_bubbles.size() || !entity.isReplace()) + return; + + // dataChanged 通知视图刷新 + // 前提:内容变更不会引起气泡高度变化,否则布局不刷新(应改用 replaceBubble)。 + auto oldBubble = m_bubbles[replaceIndex]; + if (oldBubble) { + oldBubble->setEntity(entity); + Q_EMIT dataChanged(index(replaceIndex), index(replaceIndex)); + } } -BubbleItem *BubbleModel::replaceBubble(BubbleItem *bubble) +void BubbleModel::replaceBubble(int replaceIndex, BubbleItem *bubble) { - Q_ASSERT(isReplaceBubble(bubble)); - const auto replaceIndex = replaceBubbleIndex(bubble); + if (!bubble) { + return; + } + + if (replaceIndex < 0 || replaceIndex >= m_bubbles.size()) { + qWarning() << "replaceBubble called with invalid index:" << replaceIndex; + bubble->deleteLater(); + return; + } + const auto oldBubble = m_bubbles[replaceIndex]; // Use remove + insert instead of dataChanged to force the view @@ -105,7 +141,9 @@ BubbleItem *BubbleModel::replaceBubble(BubbleItem *bubble) m_bubbles.insert(replaceIndex, bubble); endInsertRows(); - return oldBubble; + if (oldBubble) { + oldBubble->deleteLater(); + } } void BubbleModel::clear() @@ -261,22 +299,6 @@ void BubbleModel::clearInvalidBubbles() } } -int BubbleModel::replaceBubbleIndex(const BubbleItem *bubble) const -{ - if (bubble->isReplace()) { - for (int i = 0; i < m_bubbles.size(); i++) { - auto item = m_bubbles[i]; - if (item->appName() != bubble->appName()) - continue; - - if (item->bubbleId() == bubble->bubbleId()) { - return i; - } - } - } - return -1; -} - void BubbleModel::updateBubbleTimeTip() { if (m_bubbles.isEmpty()) { diff --git a/panels/notification/bubble/bubblemodel.h b/panels/notification/bubble/bubblemodel.h index b9b8f6203..1a46b2543 100644 --- a/panels/notification/bubble/bubblemodel.h +++ b/panels/notification/bubble/bubblemodel.h @@ -41,8 +41,9 @@ class BubbleModel : public QAbstractListModel public: void push(BubbleItem *bubble); - BubbleItem *replaceBubble(BubbleItem *bubble); - bool isReplaceBubble(const BubbleItem *bubble) const; + int replaceBubbleIndex(const NotifyEntity &entity) const; + void updateBubbleInPlace(int replaceIndex, const NotifyEntity &entity); + void replaceBubble(int replaceIndex, BubbleItem *bubble); QList items() const; @@ -64,7 +65,6 @@ class BubbleModel : public QAbstractListModel private: void insertBubble(BubbleItem *bubble); void updateBubbleCount(int count); - int replaceBubbleIndex(const BubbleItem *bubble) const; void updateBubbleTimeTip(); void updateContentRowCount(int rowCount); diff --git a/panels/notification/bubble/bubblepanel.cpp b/panels/notification/bubble/bubblepanel.cpp index 469fe67f7..660f3fe1e 100644 --- a/panels/notification/bubble/bubblepanel.cpp +++ b/panels/notification/bubble/bubblepanel.cpp @@ -133,17 +133,21 @@ void BubblePanel::addBubble(qint64 id) return; } + const auto replaceIndex = m_bubbles->replaceBubbleIndex(entity); + if (replaceIndex >= 0 + && entity.hints().value("x-deepin-SilentReplace").toBool()) { + m_bubbles->updateBubbleInPlace(replaceIndex, entity); + return; + } + auto bubble = new BubbleItem(entity); const auto enabled = enablePreview(entity.appId()); bubble->setEnablePreview(enabled); - if (m_bubbles->isReplaceBubble(bubble)) { - auto oldBubble = m_bubbles->replaceBubble(bubble); - if (oldBubble) { - oldBubble->deleteLater(); - } - } else { - m_bubbles->push(bubble); + if (replaceIndex >= 0) { + m_bubbles->replaceBubble(replaceIndex, bubble); + return; } + m_bubbles->push(bubble); } void BubblePanel::closeBubble(qint64 id)