From 740de9e7fbe25726bfc3cf86eb12b38cff4433d1 Mon Sep 17 00:00:00 2001 From: zhangkun Date: Mon, 30 Mar 2026 10:46:36 +0800 Subject: [PATCH] fix: fix notification bubble animation glitch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Replaced the previous complex ScriptAction with a simpler PropertyAnimation for the add transition 2. Added a new addDisplaced transition to handle displaced items when new notifications are added 3. Removed the manual script that forced previous bubble animations to complete, which was causing visual glitches 4. Now uses standard Qt Quick transitions for smoother and more reliable animation behavior fix: 修复通知气泡动画显示问题 1. 将之前复杂的 ScriptAction 替换为更简单的 PropertyAnimation 来处理添加 动画 2. 新增 addDisplaced 过渡动画来处理新通知添加时其他气泡的位移 3. 移除了强制完成前一个气泡动画的手动脚本,该脚本会导致视觉故障 4. 现在使用标准的 Qt Quick 过渡动画来实现更平滑可靠的动画效果 PMS: BUG-355029 --- frame/layershell/dlayershellwindow.cpp | 40 +++++++++++++++++++- frame/layershell/dlayershellwindow.h | 13 ++++++- frame/layershell/x11dlayershellemulation.cpp | 30 +++++++++++---- panels/notification/bubble/package/main.qml | 32 ++++++++-------- 4 files changed, 90 insertions(+), 25 deletions(-) diff --git a/frame/layershell/dlayershellwindow.cpp b/frame/layershell/dlayershellwindow.cpp index 25a184c9c..3149e005c 100644 --- a/frame/layershell/dlayershellwindow.cpp +++ b/frame/layershell/dlayershellwindow.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "dsglobal.h" @@ -40,6 +40,8 @@ class DLayerShellWindowPrivate int topMargin = 0; int bottomMargin = 0; DLayerShellWindow::ScreenConfiguration screenConfiguration = DLayerShellWindow::ScreenFromQWindow; + int preferredWidth = -1; + int preferredHeight = -1; bool closeOnDismissed = true; }; @@ -154,6 +156,42 @@ void DLayerShellWindow::setScreenConfiguration(DLayerShellWindow::ScreenConfigur } } +void DLayerShellWindow::setPreferredWidth(int width) +{ + if (width != d->preferredWidth) { + d->preferredWidth = width; + Q_EMIT geometryHintsChanged(); + } +} + +void DLayerShellWindow::resetPreferredWidth() +{ + setPreferredWidth(-1); +} + +int DLayerShellWindow::preferredWidth() const +{ + return d->preferredWidth; +} + +void DLayerShellWindow::setPreferredHeight(int height) +{ + if (height != d->preferredHeight) { + d->preferredHeight = height; + Q_EMIT geometryHintsChanged(); + } +} + +void DLayerShellWindow::resetPreferredHeight() +{ + setPreferredHeight(-1); +} + +int DLayerShellWindow::preferredHeight() const +{ + return d->preferredHeight; +} + bool DLayerShellWindow::closeOnDismissed() const { return d->closeOnDismissed; diff --git a/frame/layershell/dlayershellwindow.h b/frame/layershell/dlayershellwindow.h index c62497357..1b77ac777 100644 --- a/frame/layershell/dlayershellwindow.h +++ b/frame/layershell/dlayershellwindow.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -28,6 +28,8 @@ class DS_SHARE DLayerShellWindow : public QObject Q_PROPERTY(Layer layer READ layer WRITE setLayer NOTIFY layerChanged) Q_PROPERTY(KeyboardInteractivity keyboardInteractivity READ keyboardInteractivity WRITE setKeyboardInteractivity NOTIFY keyboardInteractivityChanged) Q_PROPERTY(ScreenConfiguration screenConfiguration READ screenConfiguration WRITE setScreenConfiguration) + Q_PROPERTY(int preferredWidth READ preferredWidth WRITE setPreferredWidth RESET resetPreferredWidth NOTIFY geometryHintsChanged) + Q_PROPERTY(int preferredHeight READ preferredHeight WRITE setPreferredHeight RESET resetPreferredHeight NOTIFY geometryHintsChanged) Q_PROPERTY(bool closeOnDismissed READ closeOnDismissed WRITE setCloseOnDismissed) @@ -111,6 +113,14 @@ class DS_SHARE DLayerShellWindow : public QObject void setScreenConfiguration(ScreenConfiguration screenConfiguration); ScreenConfiguration screenConfiguration() const; + void setPreferredWidth(int width); + void resetPreferredWidth(); + int preferredWidth() const; + + void setPreferredHeight(int height); + void resetPreferredHeight(); + int preferredHeight() const; + /** * Sets a string based identifier for this window. * This may be used by a compositor to determine stacking @@ -145,6 +155,7 @@ class DS_SHARE DLayerShellWindow : public QObject void keyboardInteractivityChanged(); void layerChanged(); void scopeChanged(); + void geometryHintsChanged(); private: DLayerShellWindow(QWindow* window); diff --git a/frame/layershell/x11dlayershellemulation.cpp b/frame/layershell/x11dlayershellemulation.cpp index c38280967..c7ae84ce4 100644 --- a/frame/layershell/x11dlayershellemulation.cpp +++ b/frame/layershell/x11dlayershellemulation.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -33,6 +33,7 @@ LayerShellEmulation::LayerShellEmulation(QWindow* window, QObject *parent) onPositionChanged(); connect(m_dlayerShellWindow, &DLayerShellWindow::anchorsChanged, this, &LayerShellEmulation::onPositionChanged); connect(m_dlayerShellWindow, &DLayerShellWindow::marginsChanged, this, &LayerShellEmulation::onPositionChanged); + connect(m_dlayerShellWindow, &DLayerShellWindow::geometryHintsChanged, this, &LayerShellEmulation::onPositionChanged); onExclusionZoneChanged(); m_exclusionZoneChangedTimer.setSingleShot(true); @@ -117,17 +118,30 @@ void LayerShellEmulation::onPositionChanged() { auto anchors = m_dlayerShellWindow->anchors(); auto screen = m_window->screen(); + if (!screen) { + return; + } + + int targetWidth = m_window->width(); + int targetHeight = m_window->height(); + if (m_dlayerShellWindow->preferredWidth() > 0) { + targetWidth = m_dlayerShellWindow->preferredWidth(); + } + if (m_dlayerShellWindow->preferredHeight() > 0) { + targetHeight = m_dlayerShellWindow->preferredHeight(); + } + auto screenRect = screen->geometry(); - auto x = screenRect.left() + (screenRect.width() - m_window->width()) / 2; - auto y = screenRect.top() + (screenRect.height() - m_window->height()) / 2; + auto x = screenRect.left() + (screenRect.width() - targetWidth) / 2; + auto y = screenRect.top() + (screenRect.height() - targetHeight) / 2; if (anchors & DLayerShellWindow::AnchorRight) { // https://doc.qt.io/qt-6/qrect.html#right - x = (screen->geometry().right() + 1 - m_window->width() - m_dlayerShellWindow->rightMargin()); + x = (screen->geometry().right() + 1 - targetWidth - m_dlayerShellWindow->rightMargin()); } if (anchors & DLayerShellWindow::AnchorBottom) { // https://doc.qt.io/qt-6/qrect.html#bottom - y = (screen->geometry().bottom() + 1 - m_window->height() - m_dlayerShellWindow->bottomMargin()); + y = (screen->geometry().bottom() + 1 - targetHeight - m_dlayerShellWindow->bottomMargin()); } if (anchors & DLayerShellWindow::AnchorLeft) { x = (screen->geometry().left() + m_dlayerShellWindow->leftMargin()); @@ -136,7 +150,7 @@ void LayerShellEmulation::onPositionChanged() y = (screen->geometry().top() + m_dlayerShellWindow->topMargin()); } - QRect rect(x, y, m_window->width(), m_window->height()); + QRect rect(x, y, targetWidth, targetHeight); const bool horizontallyConstrained = anchors.testFlags({DLayerShellWindow::AnchorLeft, DLayerShellWindow::AnchorRight}); const bool verticallyConstrained = anchors.testFlags({DLayerShellWindow::AnchorTop, DLayerShellWindow::AnchorBottom}); @@ -150,7 +164,9 @@ void LayerShellEmulation::onPositionChanged() rect.setHeight(screen->geometry().height() - m_dlayerShellWindow->topMargin() - m_dlayerShellWindow->bottomMargin()); } - m_window->setGeometry(rect); + if (m_window->geometry() != rect) { + m_window->setGeometry(rect); + } } /** diff --git a/panels/notification/bubble/package/main.qml b/panels/notification/bubble/package/main.qml index d25dabf91..18dd17787 100644 --- a/panels/notification/bubble/package/main.qml +++ b/panels/notification/bubble/package/main.qml @@ -68,8 +68,8 @@ Window { } visible: Applet.visible - width: 390 - height: Math.max(10, bubbleView.height + bubbleView.anchors.topMargin + bubbleView.anchors.bottomMargin) + DLayerShellWindow.preferredWidth: 390 + DLayerShellWindow.preferredHeight: Math.max(10, bubbleView.height + bubbleView.anchors.topMargin + bubbleView.anchors.bottomMargin) DLayerShellWindow.layer: DLayerShellWindow.LayerOverlay DLayerShellWindow.anchors: DLayerShellWindow.AnchorBottom | DLayerShellWindow.AnchorRight DLayerShellWindow.topMargin: windowMargin(0) @@ -107,27 +107,27 @@ Window { verticalLayoutDirection: ListView.BottomToTop add: Transition { id: addTrans - // Before starting the new animation, forcibly complete the previous notification bubble's animation - ScriptAction { - script: { - // Only handle the previous notification bubble (at index count - 1); no need to iterate through all of them - if (bubbleView.count > 1) { - let prevItem = bubbleView.itemAtIndex(bubbleView.count - 2) - if (prevItem) { - // Directly set x to 0 to forcibly complete the animation - prevItem.x = 0 - } - } - } - } - XAnimator { + PropertyAnimation { target: addTrans.ViewTransition.item + properties: "x" from: addTrans.ViewTransition.item.width to: 0 duration: 600 easing.type: Easing.OutExpo } } + + addDisplaced: Transition { + id: addDisplacedTrans + PropertyAnimation { + target: addDisplacedTrans.ViewTransition.item + properties: "x" + to: 0 + duration: 600 + easing.type: Easing.OutExpo + } + } + delegate: Bubble { width: 360 bubble: model