From 607a1eec2eb33a03fbf413fdf42e99bf36c4c69f Mon Sep 17 00:00:00 2001 From: deepin-wm Date: Tue, 29 Sep 2026 14:33:30 +0800 Subject: [PATCH 1/2] feat(wallpaper): add curtain-style transition for wallpaper switching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将壁纸切换过渡从交叉淡化改为拉幕式效果:新壁纸在 wallpaperReady 后从屏幕右侧滑入覆盖旧壁纸,滑动结束后恢复 anchors.fill 布局并 清理旧槽位,同时避免快速连续切换时对动画中槽位的悬空删除。 Switch the wallpaper transition from cross-fade to a curtain effect: once the new wallpaper surface is ready, it slides in from the right edge to cover the old one; anchors.fill is restored and the old slot is cleaned up when the slide finishes. Log: 壁纸切换改为拉幕式过渡动画 Influence: 壁纸切换动画由交叉淡化改为拉幕式滑入,过渡更自然。 Multica Issue: WM-557 --- src/wallpaper/wallpaperswitcheritem.cpp | 49 ++++++++++++++++--------- src/wallpaper/wallpaperswitcheritem.h | 4 +- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/wallpaper/wallpaperswitcheritem.cpp b/src/wallpaper/wallpaperswitcheritem.cpp index 816dba4f8f..59dc4c35e5 100644 --- a/src/wallpaper/wallpaperswitcheritem.cpp +++ b/src/wallpaper/wallpaperswitcheritem.cpp @@ -171,8 +171,7 @@ void WallpaperSwitcherItem::handleWorkspaceAdded() void WallpaperSwitcherItem::switchToNewSlot() { auto *newSlot = new WallpaperSlot(this); - QQuickItemPrivate::get(newSlot)->anchors()->setFill(this); - newSlot->setOpacity(0); + newSlot->setVisible(false); newSlot->setOutput(m_output); newSlot->setWorkspace(m_workspace); @@ -181,46 +180,60 @@ void WallpaperSwitcherItem::switchToNewSlot() return; } + // Curtain effect: park the new wallpaper fully off-screen right, then + // slide it over the old one like a curtain being drawn across. + newSlot->setWidth(width()); + newSlot->setHeight(height()); + newSlot->setX(width()); + + if (m_oldSlot) + m_oldSlot->deleteLater(); m_oldSlot = m_currentSlot; m_currentSlot = newSlot; Q_EMIT sourceChanged(); - auto *fadeOut = new QPropertyAnimation(m_oldSlot, "opacity"); - fadeOut->setDuration(m_opacityDuration); - fadeOut->setStartValue(1.0); - fadeOut->setEndValue(0.0); - fadeOut->setEasingCurve(QEasingCurve::InOutQuad); - connect(fadeOut, &QPropertyAnimation::finished, this, &WallpaperSwitcherItem::onAnimationFinished); - fadeOut->start(QAbstractAnimation::DeleteWhenStopped); - auto *interface = TreelandWallpaperSurfaceInterfaceV1::get(newSlot->source()); if (interface && interface->wallpaperReady()) { - startFadeIn(newSlot); + startSlideIn(newSlot); } else if (interface) { connect(interface, &TreelandWallpaperSurfaceInterfaceV1::ready, this, [this, newSlot]() { if (m_currentSlot == newSlot) - startFadeIn(newSlot); + startSlideIn(newSlot); }, Qt::SingleShotConnection); } } -void WallpaperSwitcherItem::startFadeIn(WallpaperSlot *slot) +void WallpaperSwitcherItem::startSlideIn(WallpaperSlot *slot) { - auto *anim = new QPropertyAnimation(slot, "opacity"); + if (width() <= 0.0 || height() <= 0.0) { + finishSlideIn(slot); + return; + } + + slot->setVisible(true); + auto *anim = new QPropertyAnimation(slot, "x"); anim->setDuration(m_opacityDuration); - anim->setStartValue(0.0); - anim->setEndValue(1.0); - anim->setEasingCurve(QEasingCurve::InOutQuad); + anim->setStartValue(width()); + anim->setEndValue(0.0); + anim->setEasingCurve(QEasingCurve::OutCubic); + connect(anim, &QPropertyAnimation::finished, this, [this, slot]() { + finishSlideIn(slot); + }); anim->start(QAbstractAnimation::DeleteWhenStopped); } -void WallpaperSwitcherItem::onAnimationFinished() +void WallpaperSwitcherItem::finishSlideIn(WallpaperSlot *slot) { + if (m_currentSlot != slot) + return; + + slot->setX(0); + QQuickItemPrivate::get(slot)->anchors()->setFill(this); if (m_oldSlot) { m_oldSlot->deleteLater(); m_oldSlot = nullptr; diff --git a/src/wallpaper/wallpaperswitcheritem.h b/src/wallpaper/wallpaperswitcheritem.h index 1ac57cee03..0734b93f91 100644 --- a/src/wallpaper/wallpaperswitcheritem.h +++ b/src/wallpaper/wallpaperswitcheritem.h @@ -62,8 +62,8 @@ class WallpaperSwitcherItem : public QQuickItem void handleWallpaperUpdate(); void handleWorkspaceAdded(); void switchToNewSlot(); - void onAnimationFinished(); - void startFadeIn(WallpaperSlot *slot); + void startSlideIn(WallpaperSlot *slot); + void finishSlideIn(WallpaperSlot *slot); QPointer m_workspace; QPointer m_output; From d9966c8b06170877a6f26e6a99470ec73a4f1139 Mon Sep 17 00:00:00 2001 From: deepin-wm Date: Tue, 29 Sep 2026 14:56:29 +0800 Subject: [PATCH 2/2] fix(wallpaper): keep a visible wallpaper slot across rapid switches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复评审问题 1/2/3 并采纳问题 4: - finishSlideIn 恢复槽位可见性,修复零尺寸分支下壁纸区域永久黑屏; - 切换前先停止在途动画让当前槽位原地完成,已显示的被顶替槽位 升级为 backdrop、未显示的停靠槽位直接回收,保证新壁纸就绪前 屏幕上始终有一个全覆盖的可见壁纸; - 用显式 shown 标记代替 isVisible() 判断,避免祖先不可见时误删; - ready/finished 回调改捕 QPointer,析构时先停动画; - opacityDuration 更名 transitionDuration(QML 无调用方)。 Fixes from code review: restore slot visibility in finishSlideIn to avoid a permanent black screen in the zero-size path; stop the in-flight slide before superseding so a shown slot always remains as backdrop while the next wallpaper waits for ready; use an explicit shown flag instead of isVisible(); guard callbacks with QPointer; rename opacityDuration to transitionDuration. Log: 修复快速切换壁纸槽位可见性问题 Influence: 快速切换中始终保留可见壁纸槽位,消除黑屏回归。 Multica Issue: WM-557 --- src/wallpaper/wallpaperswitcheritem.cpp | 51 +++++++++++++++++-------- src/wallpaper/wallpaperswitcheritem.h | 13 ++++--- 2 files changed, 44 insertions(+), 20 deletions(-) diff --git a/src/wallpaper/wallpaperswitcheritem.cpp b/src/wallpaper/wallpaperswitcheritem.cpp index 59dc4c35e5..8e5b5216a7 100644 --- a/src/wallpaper/wallpaperswitcheritem.cpp +++ b/src/wallpaper/wallpaperswitcheritem.cpp @@ -28,6 +28,8 @@ class WallpaperSlot : public WallpaperItem { setLive(true); } + // False only while parked off-screen before its first reveal. + bool shown = true; }; WallpaperSwitcherItem::WallpaperSwitcherItem(QQuickItem *parent) @@ -52,6 +54,8 @@ WallpaperSwitcherItem::WallpaperSwitcherItem(QQuickItem *parent) WallpaperSwitcherItem::~WallpaperSwitcherItem() { + if (m_slideAnim) + m_slideAnim->stop(); // finish handler still has live slots here delete m_oldSlot; delete m_currentSlot; } @@ -118,18 +122,18 @@ QString WallpaperSwitcherItem::source() const return m_currentSlot ? m_currentSlot->source() : QString(); } -int WallpaperSwitcherItem::opacityDuration() const +int WallpaperSwitcherItem::transitionDuration() const { - return m_opacityDuration; + return m_transitionDuration; } -void WallpaperSwitcherItem::setOpacityDuration(int duration) +void WallpaperSwitcherItem::setTransitionDuration(int duration) { - if (m_opacityDuration == duration) + if (m_transitionDuration == duration) return; - m_opacityDuration = duration; - Q_EMIT opacityDurationChanged(); + m_transitionDuration = duration; + Q_EMIT transitionDurationChanged(); } void WallpaperSwitcherItem::slowDown() @@ -172,6 +176,7 @@ void WallpaperSwitcherItem::switchToNewSlot() { auto *newSlot = new WallpaperSlot(this); newSlot->setVisible(false); + newSlot->shown = false; newSlot->setOutput(m_output); newSlot->setWorkspace(m_workspace); @@ -186,9 +191,21 @@ void WallpaperSwitcherItem::switchToNewSlot() newSlot->setHeight(height()); newSlot->setX(width()); - if (m_oldSlot) - m_oldSlot->deleteLater(); - m_oldSlot = m_currentSlot; + // Settle any in-flight transition first so the screen always keeps a + // fully visible wallpaper while the next one waits for ready. + if (m_slideAnim) { + m_slideAnim->stop(); // finished -> finishSlideIn completes the current slot + m_slideAnim = nullptr; + } + if (m_currentSlot) { + if (m_currentSlot->shown) { + if (m_oldSlot) + m_oldSlot->deleteLater(); + m_oldSlot = m_currentSlot; + } else { + m_currentSlot->deleteLater(); // parked, never shown + } + } m_currentSlot = newSlot; Q_EMIT sourceChanged(); @@ -200,9 +217,9 @@ void WallpaperSwitcherItem::switchToNewSlot() connect(interface, &TreelandWallpaperSurfaceInterfaceV1::ready, this, - [this, newSlot]() { - if (m_currentSlot == newSlot) - startSlideIn(newSlot); + [this, guard = QPointer(newSlot)]() { + if (guard && m_currentSlot == guard.data()) + startSlideIn(guard); }, Qt::SingleShotConnection); } @@ -215,15 +232,17 @@ void WallpaperSwitcherItem::startSlideIn(WallpaperSlot *slot) return; } + slot->shown = true; slot->setVisible(true); auto *anim = new QPropertyAnimation(slot, "x"); - anim->setDuration(m_opacityDuration); + anim->setDuration(m_transitionDuration); anim->setStartValue(width()); anim->setEndValue(0.0); anim->setEasingCurve(QEasingCurve::OutCubic); - connect(anim, &QPropertyAnimation::finished, this, [this, slot]() { - finishSlideIn(slot); + connect(anim, &QPropertyAnimation::finished, this, [this, guard = QPointer(slot)]() { + finishSlideIn(guard); }); + m_slideAnim = anim; anim->start(QAbstractAnimation::DeleteWhenStopped); } @@ -232,6 +251,8 @@ void WallpaperSwitcherItem::finishSlideIn(WallpaperSlot *slot) if (m_currentSlot != slot) return; + slot->shown = true; + slot->setVisible(true); slot->setX(0); QQuickItemPrivate::get(slot)->anchors()->setFill(this); if (m_oldSlot) { diff --git a/src/wallpaper/wallpaperswitcheritem.h b/src/wallpaper/wallpaperswitcheritem.h index 0734b93f91..edf336468e 100644 --- a/src/wallpaper/wallpaperswitcheritem.h +++ b/src/wallpaper/wallpaperswitcheritem.h @@ -8,6 +8,8 @@ Q_MOC_INCLUDE("workspace/workspace.h") +class QPropertyAnimation; + class WallpaperSlot; class TreelandWallpaperSurfaceInterfaceV1; @@ -26,7 +28,7 @@ class WallpaperSwitcherItem : public QQuickItem Q_PROPERTY(WorkspaceModel *workspace READ workspace WRITE setWorkspace NOTIFY workspaceChanged FINAL) Q_PROPERTY(bool play READ play WRITE setPlay NOTIFY playChanged FINAL) Q_PROPERTY(QString source READ source NOTIFY sourceChanged FINAL) - Q_PROPERTY(int opacityDuration READ opacityDuration WRITE setOpacityDuration NOTIFY opacityDurationChanged FINAL) + Q_PROPERTY(int transitionDuration READ transitionDuration WRITE setTransitionDuration NOTIFY transitionDurationChanged FINAL) QML_NAMED_ELEMENT(WallpaperSwitcher) QML_ADDED_IN_VERSION(1, 0) @@ -46,8 +48,8 @@ class WallpaperSwitcherItem : public QQuickItem QString source() const; - int opacityDuration() const; - void setOpacityDuration(int duration); + int transitionDuration() const; + void setTransitionDuration(int duration); Q_INVOKABLE void slowDown(); @@ -56,7 +58,7 @@ class WallpaperSwitcherItem : public QQuickItem void workspaceChanged(); void playChanged(); void sourceChanged(); - void opacityDurationChanged(); + void transitionDurationChanged(); private: void handleWallpaperUpdate(); @@ -69,8 +71,9 @@ class WallpaperSwitcherItem : public QQuickItem QPointer m_output; bool m_play = true; QString m_source; - int m_opacityDuration = 500; + int m_transitionDuration = 500; + QPointer m_slideAnim; WallpaperSlot *m_currentSlot = nullptr; WallpaperSlot *m_oldSlot = nullptr; };