Skip to content

feat: add bubble removal animation#1461

Closed
qxp930712 wants to merge 1 commit intolinuxdeepin:masterfrom
qxp930712:master
Closed

feat: add bubble removal animation#1461
qxp930712 wants to merge 1 commit intolinuxdeepin:masterfrom
qxp930712:master

Conversation

@qxp930712
Copy link

  1. Implement asynchronous removal logic in BubbleModel to support QML animations
  2. Add bubbleAboutToRemove signal and removeAnimationDuration property
  3. Delay actual data removal until animation completes using QTimer::singleShot
  4. Introduce slide-out and fade-out transitions in Bubble.qml and main.qml
  5. Set ListView cacheBuffer to 0 to ensure proper transition rendering
  6. Add removeDisplaced transition for smooth repositioning of remaining items

Log: Notification bubbles will now slide out smoothly when closed instead of disappearing instantly.

Influence:

  1. Close a notification bubble and observe the slide-out and fade animation
  2. Verify the bubble is only removed from the model after the animation finishes
  3. Test removing multiple bubbles rapidly to check for visual artifacts
  4. Verify that remaining bubbles slide up smoothly when a bubble is removed
  5. Check model consistency after multiple remove operations
  6. Confirm that hovering over a bubble does not interrupt the removal animation

PMS: BUG-284659

@deepin-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: qxp930712

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @qxp930712, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

m_delayBubbles.removeAll(id);
remove(m_bubbles.indexOf(item));
// Emit signal before removing to trigger QML animation
Q_EMIT bubbleAboutToRemove(id);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@deepin-bot
Copy link

deepin-bot bot commented Mar 5, 2026

TAG Bot

New tag: 2.0.31
DISTRIBUTION: unstable
Suggest: synchronizing this PR through rebase #1481

Changed the bubble panel visibility logic to include a 400ms delay
before hiding when there are no notifications. This prevents the panel
from disappearing abruptly when the last bubble is removed. The QML
ListView now includes a remove transition with smooth exit animation
for bubbles.

Added QTimer include for delayed hide functionality. Modified the
ListView height calculation to use maximum of contentHeight and
childrenRect.height to ensure proper layout during animations.
Implemented a sequential animation for bubble removal with x-axis slide-
out effect.

Log: Improved notification bubble animations with smoother hide effects

Influence:
1. Test notification bubble appearance and disappearance
2. Verify panel remains visible during bubble removal animations
3. Check that multiple bubbles animate correctly
4. Test edge cases with rapid notification additions/removals
5. Verify panel properly hides after all bubbles are removed
6. Test animation timing and smoothness

PMS: BUG-284659
@deepin-ci-robot
Copy link

deepin pr auto review

这段代码修改主要是为了优化通知气泡的显示与隐藏动画效果,使其更加平滑。以下是对代码的详细审查和改进建议:

1. 语法与逻辑审查

C++ 部分 (bubblepanel.cpp)

  • 语法正确性:代码引入了 <qtimer.h>,语法符合 Qt/C++ 标准,逻辑上通过 QTimer::singleShot 延迟执行 setVisible(false),配合 QML 的移除动画时长(400ms),逻辑是自洽的。
  • 逻辑分析
    • visiblefalse 时,设置 400ms 的定时器来隐藏面板。这给了 QML 中的气泡移除动画(也是 400ms)足够的时间播放完毕。
    • visibletrue 时,立即显示面板。
    • 潜在逻辑缺陷:如果在 400ms 的延迟期间,新的通知到来(即 isEmpty 变为 false),onBubbleCountChanged 可能会被再次调用。此时会执行 setVisible(true)。虽然这能恢复显示,但之前的定时器回调仍然会排队等待执行。这意味着在动画结束后,面板可能会再次被错误地隐藏。

QML 部分 (main.qml)

  • 语法正确性:QML 语法正确,使用了 Transition 来定义移除动画。
  • 逻辑分析
    • remove 中的 ListView.delayRemove 设置为 true 是必须的,这确保了在动画播放期间项目不会被立即销毁。
    • 动画时长设置为 400ms,与 C++ 中的延迟时间匹配,逻辑上是合理的。
    • 高度计算height: Math.max(contentHeight, childrenRect.height)。通常 contentHeight 已经足够,但在某些布局更新时刻,显式使用 childrenRect.height 可能是为了确保高度不为 0 或处理边界情况。不过,contentHeight 通常是更推荐的方式,因为它考虑了 headerfooter。如果是为了修复特定的高度塌陷问题,这个改动是可以接受的。

2. 代码质量改进建议

针对 C++ 逻辑缺陷的修复(竞态条件)

建议在 BubblePanel 类中维护一个 QTimer 成员变量,而不是使用静态函数 QTimer::singleShot。这样可以有效地在新的通知到来时取消之前的隐藏操作。

改进后的 C++ 代码示例:

// 在 bubblepanel.h 头文件中
class BubblePanel : public ... {
    Q_OBJECT
    // ...
private:
    QTimer *m_hideTimer; // 声明一个定时器成员变量
    // ...
};

// 在 bubblepanel.cpp 构造函数中
BubblePanel::BubblePanel(...) {
    // ...
    m_hideTimer = new QTimer(this);
    m_hideTimer->setSingleShot(true);
    connect(m_hideTimer, &QTimer::timeout, this, [this]() {
        if (m_bubbles->items().isEmpty()) {
            setVisible(false);
        }
    });
    // ...
}

// 修改 onBubbleCountChanged 实现
void BubblePanel::onBubbleCountChanged()
{
    bool isEmpty = m_bubbles->items().isEmpty();
    const bool visible = !isEmpty && enabled();
    
    if (visible) {
        // 如果需要显示,停止可能正在运行的隐藏定时器,并立即显示
        m_hideTimer->stop();
        setVisible(true);
    } else {
        // 如果需要隐藏,启动定时器(如果已经在运行,restart 会重置时间)
        // 这里的 400ms 应与 QML 动画时长保持一致
        m_hideTimer->start(400);
    }
}

改进点解释:

  1. 防止竞态:当气泡列表为空启动定时器后,如果在 400ms 内有新气泡加入,visible 变为 true,代码会调用 m_hideTimer->stop(),从而阻止面板被错误隐藏。
  2. 双重检查:在定时器的 lambda 回调中再次检查 isEmpty 是一个很好的防御性编程习惯,防止极端情况下的状态不一致。
  3. 成员变量管理:将 QTimer 作为成员变量并由父对象(this)管理内存,避免了静态函数回调带来的上下文控制困难和潜在的内存管理问题。

3. 代码性能

  • C++:引入 QTimer 成员变量的性能开销极小,且仅在状态变化时触发,不会影响性能。
  • QML
    • Math.max(contentHeight, childrenRect.height)childrenRect.height 的频繁计算可能会触发额外的布局传递。如果 contentHeight 在大多数情况下表现良好,建议优先使用 contentHeight。如果必须使用 childrenRect,请确保其父级布局不要过于复杂,以免造成性能瓶颈。
    • 动画使用了 NumberAnimation,这是性能开销最小的动画方式之一,没有问题。

4. 代码安全

  • 内存安全:原代码使用 QTimer::singleShot 传递 this 指针,依赖 Qt 的自动连接机制。如果 BubblePanel 在定时器触发前被销毁,Qt 会断开连接,通常是安全的。但改进方案中使用成员变量并由 this 作为父对象,内存管理更加明确和安全。
  • 空指针检查:在改进代码中,定时器回调内增加了对 m_bubbles 是否为空的二次检查,增强了代码的健壮性。

总结

这段代码的初衷是好的(添加平滑过渡动画),但原实现存在一个并发/时序逻辑漏洞(在动画播放期间新通知到达可能导致面板闪烁或错误隐藏)。

建议采纳上述关于引入 QTimer 成员变量并处理 stop() 逻辑的改进方案,这将显著提升代码的健壮性和用户体验。

@qxp930712 qxp930712 closed this Mar 10, 2026
@qxp930712
Copy link
Author

修改方案

@qxp930712 qxp930712 reopened this Mar 10, 2026
@qxp930712 qxp930712 closed this Mar 10, 2026
@qxp930712
Copy link
Author

修改方案

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants