diff --git a/panels/notification/bubble/bubblemodel.cpp b/panels/notification/bubble/bubblemodel.cpp index 1bcc6ee82..5f8cc223f 100644 --- a/panels/notification/bubble/bubblemodel.cpp +++ b/panels/notification/bubble/bubblemodel.cpp @@ -137,7 +137,17 @@ BubbleItem *BubbleModel::removeById(qint64 id) for (const auto &item : m_bubbles) { if (item->id() == id) { m_delayBubbles.removeAll(id); - remove(m_bubbles.indexOf(item)); + // Emit signal before removing to trigger QML animation + Q_EMIT bubbleAboutToRemove(id); + // Delay the actual removal to allow animation to complete + QTimer::singleShot(m_removeAnimationDuration, this, [this, id]() { + for (const auto &item : m_bubbles) { + if (item->id() == id) { + remove(m_bubbles.indexOf(item)); + break; + } + } + }); return item; } } @@ -145,6 +155,17 @@ BubbleItem *BubbleModel::removeById(qint64 id) return nullptr; } +int BubbleModel::removeAnimationDuration() const +{ + return m_removeAnimationDuration; +} + +void BubbleModel::setRemoveAnimationDuration(int duration) +{ + m_removeAnimationDuration = duration + 100; + Q_EMIT removeAnimationDurationChanged(); +} + BubbleItem *BubbleModel::bubbleItem(int bubbleIndex) const { if (bubbleIndex < 0 || bubbleIndex >= items().count()) diff --git a/panels/notification/bubble/bubblemodel.h b/panels/notification/bubble/bubblemodel.h index 5977775ff..bf81528de 100644 --- a/panels/notification/bubble/bubblemodel.h +++ b/panels/notification/bubble/bubblemodel.h @@ -18,6 +18,7 @@ class BubbleModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(qint64 delayRemovedBubble READ delayRemovedBubble WRITE setDelayRemovedBubble NOTIFY delayRemovedBubbleChanged FINAL) + Q_PROPERTY(int removeAnimationDuration READ removeAnimationDuration WRITE setRemoveAnimationDuration NOTIFY removeAnimationDurationChanged FINAL) public: enum { AppName = Qt::UserRole + 1, @@ -64,10 +65,15 @@ class BubbleModel : public QAbstractListModel qint64 delayRemovedBubble() const; void setDelayRemovedBubble(qint64 newDelayRemovedBubble); + int removeAnimationDuration() const; + void setRemoveAnimationDuration(int duration); + void clearInvalidBubbles(); signals: void delayRemovedBubbleChanged(); + void bubbleAboutToRemove(qint64 id); + void removeAnimationDurationChanged(); private: void updateBubbleCount(int count); @@ -85,6 +91,7 @@ class BubbleModel : public QAbstractListModel QList m_delayBubbles; qint64 m_delayRemovedBubble{NotifyEntity::InvalidId}; const int DelayRemovBubbleTime{1000}; + int m_removeAnimationDuration{700}; }; } diff --git a/panels/notification/bubble/package/Bubble.qml b/panels/notification/bubble/package/Bubble.qml index 4a78cbedc..50b506631 100644 --- a/panels/notification/bubble/package/Bubble.qml +++ b/panels/notification/bubble/package/Bubble.qml @@ -12,6 +12,17 @@ Control { id: control height: loader.height property var bubble + property bool isRemoving: false + + Connections { + target: Applet.bubbles + function onBubbleAboutToRemove(id) { + if (id === bubble.id) { + control.isRemoving = true + } + } + } + onHoveredChanged: function () { if (control.hovered) { Applet.bubbles.delayRemovedBubble = bubble.id @@ -19,6 +30,29 @@ Control { Applet.bubbles.delayRemovedBubble = NotifyEntity.InvalidId } } + + states: [ + State { + name: "removing" + when: control.isRemoving + PropertyChanges { + target: control + x: control.width + opacity: 0 + } + } + ] + + transitions: [ + Transition { + to: "removing" + NumberAnimation { + properties: "x,opacity" + duration: Applet.bubbles.removeAnimationDuration + easing.type: Easing.InExpo + } + } + ] Loader { id: loader diff --git a/panels/notification/bubble/package/main.qml b/panels/notification/bubble/package/main.qml index 80ccac395..bea9256b7 100644 --- a/panels/notification/bubble/package/main.qml +++ b/panels/notification/bubble/package/main.qml @@ -12,6 +12,12 @@ import org.deepin.dtk 1.0 Window { id: root + + readonly property int removeAnimationDuration: 600 + + Component.onCompleted: { + Applet.bubbles.removeAnimationDuration = removeAnimationDuration + } function windowMargin(position) { let dockApplet = DS.applet("org.deepin.ds.dock") @@ -105,6 +111,7 @@ Window { model: Applet.bubbles interactive: false verticalLayoutDirection: ListView.BottomToTop + cacheBuffer: 0 add: Transition { id: addTrans // Before starting the new animation, forcibly complete the previous notification bubble's animation @@ -128,6 +135,31 @@ Window { easing.type: Easing.OutExpo } } + remove: Transition { + NumberAnimation { + target: removeTrans.ViewTransition.item + property: "x" + from: 0 + to: removeTrans.ViewTransition.item.width + duration: root.removeAnimationDuration + easing.type: Easing.InExpo + } + NumberAnimation { + target: removeTrans.ViewTransition.item + property: "opacity" + from: 1.0 + to: 0.0 + duration: root.removeAnimationDuration + easing.type: Easing.InExpo + } + } + removeDisplaced: Transition { + NumberAnimation { + properties: "y" + duration: 400 + easing.type: Easing.OutCubic + } + } delegate: Bubble { width: 360 bubble: model