From 57123a5c8c5b373b08399bdb8043e128c9406664 Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Tue, 2 Jun 2026 18:35:54 +1000 Subject: [PATCH] fix: animate lock cover art --- components/images/FadeImage.qml | 56 +++++++++++++++++++++++++++++++++ components/widgets/CoverArt.qml | 53 ++----------------------------- modules/lock/Media.qml | 3 +- 3 files changed, 60 insertions(+), 52 deletions(-) create mode 100644 components/images/FadeImage.qml diff --git a/components/images/FadeImage.qml b/components/images/FadeImage.qml new file mode 100644 index 00000000..ebbb9ab6 --- /dev/null +++ b/components/images/FadeImage.qml @@ -0,0 +1,56 @@ +import QtQuick +import Quickshell +import qs.components + +Image { + id: root + + property bool hadPrevious + + function maybeStartInAnim(): void { + if (!opacityInAnim.running && status === Image.Ready) { + opacityInAnim.type = hadPrevious ? Anim.DefaultEffects : Anim.StandardLarge; + opacityInAnim.start(); + } + } + + asynchronous: true + fillMode: Image.PreserveAspectCrop + + sourceSize: { + const dpr = (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1; + return Qt.size(width * dpr, height * dpr); + } + + retainWhileLoading: true + opacity: 0 + + onStatusChanged: maybeStartInAnim() + + Anim on opacity { + id: opacityInAnim + + running: false + to: 1 + } + + Behavior on source { + SequentialAnimation { + Anim { + target: root + property: "opacity" + to: 0 + type: Anim.FastEffects + } + PropertyAction { + target: root + property: "hadPrevious" + value: root.source + } + PropertyAction {} + ScriptAction { + script: root.maybeStartInAnim() + } + } + } +} diff --git a/components/widgets/CoverArt.qml b/components/widgets/CoverArt.qml index e323bed7..589ed43a 100644 --- a/components/widgets/CoverArt.qml +++ b/components/widgets/CoverArt.qml @@ -8,6 +8,7 @@ import Caelestia.Config import qs.components import qs.components.controls import qs.components.effects +import qs.components.images import qs.services Item { @@ -92,66 +93,16 @@ Item { } } - Image { + FadeImage { id: image anchors.fill: parent source: Players.getArtUrl(Players.active) - asynchronous: true - fillMode: Image.PreserveAspectCrop - - sourceSize: { - const dpr = (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1; - return Qt.size(width * dpr, height * dpr); - } layer.enabled: true layer.effect: Mask { maskSource: shapeWrapper } - - retainWhileLoading: true - opacity: 0 - - onStatusChanged: { - if (!opacityInAnim.running && image.status === Image.Ready) { - opacityInAnim.type = root.hadPrevious ? Anim.DefaultEffects : Anim.StandardLarge; - opacityInAnim.start(); - } - } - - Anim on opacity { - id: opacityInAnim - - running: false - to: 1 - type: Anim.DefaultEffects - } - - Behavior on source { - SequentialAnimation { - Anim { - target: image - property: "opacity" - to: 0 - type: Anim.FastEffects - } - PropertyAction { - target: root - property: "hadPrevious" - value: image.source - } - PropertyAction {} - ScriptAction { - script: { - if (!opacityInAnim.running && image.status === Image.Ready) { - opacityInAnim.type = root.hadPrevious ? Anim.DefaultEffects : Anim.StandardLarge; - opacityInAnim.start(); - } - } - } - } - } } } diff --git a/modules/lock/Media.qml b/modules/lock/Media.qml index 05756a8b..649032e3 100644 --- a/modules/lock/Media.qml +++ b/modules/lock/Media.qml @@ -5,6 +5,7 @@ import Caelestia.Components import Caelestia.Config import qs.components import qs.components.controls +import qs.components.images import qs.services StyledClippingRect { @@ -16,7 +17,7 @@ StyledClippingRect { radius: Tokens.rounding.extraLarge color: Colours.tPalette.m3surfaceContainer - Image { + FadeImage { anchors.fill: parent source: Players.getArtUrl(Players.active)