From 543388f229d5e8fd5134642ac4340326e3765057 Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Tue, 5 May 2026 18:38:38 +1000 Subject: [PATCH] feat: add lyrics info --- modules/dashboard/media/LyricsAndSelector.qml | 8 +- modules/dashboard/media/LyricsInfo.qml | 221 ++++++++++++++++++ 2 files changed, 223 insertions(+), 6 deletions(-) create mode 100644 modules/dashboard/media/LyricsInfo.qml diff --git a/modules/dashboard/media/LyricsAndSelector.qml b/modules/dashboard/media/LyricsAndSelector.qml index 1f226e6e..89d7ce5f 100644 --- a/modules/dashboard/media/LyricsAndSelector.qml +++ b/modules/dashboard/media/LyricsAndSelector.qml @@ -18,6 +18,7 @@ Item { RowLayout { Layout.bottomMargin: -Tokens.spacing.medium spacing: Tokens.spacing.medium + z: 1 MaterialIcon { Layout.topMargin: Math.round(fontInfo.pointSize * 0.12) @@ -31,12 +32,7 @@ Item { font: Tokens.font.title.medium } - IconButton { - icon: "settings" - isToggle: true - isRound: true - type: IconButton.Tonal - } + LyricsInfo {} } LyricList { diff --git a/modules/dashboard/media/LyricsInfo.qml b/modules/dashboard/media/LyricsInfo.qml new file mode 100644 index 00000000..d50adaac --- /dev/null +++ b/modules/dashboard/media/LyricsInfo.qml @@ -0,0 +1,221 @@ +import QtQuick +import QtQuick.Layouts +import Caelestia.Blobs +import Caelestia.Config +import Caelestia.Services +import qs.components +import qs.services + +Item { + id: root + + property bool open + readonly property real padding: Tokens.padding.large + + implicitWidth: btn.implicitWidth + implicitHeight: btn.implicitHeight + + BlobGroup { + id: blobGroup + + color: Colours.palette.m3surfaceContainerHighest + smoothing: 12 + + Behavior on color { + CAnim {} + } + } + + BlobRect { + id: btnRect + + anchors.fill: parent + anchors.margins: !btn.pressed && btn.containsMouse ? -Tokens.padding.extraSmall : 0 + group: blobGroup + radius: Tokens.rounding.large + + Behavior on anchors.margins { + Anim {} + } + } + + BlobRect { + id: rect + + x: (btnRect.width - implicitWidth) * 0.7 + y: (btnRect.height - implicitHeight) * 0.3 + implicitWidth: btnRect.width * 0.4 + implicitHeight: btnRect.height * 0.4 + + group: blobGroup + radius: Tokens.rounding.large + deformScale: 0.00001 + + states: State { + name: "open" + when: root.open + + PropertyChanges { + rect.x: -(layout.implicitWidth + root.padding + root.Tokens.padding.medium) + rect.y: -root.Tokens.padding.medium + rect.implicitWidth: Math.max(layout.implicitWidth, placeholder.implicitWidth) + root.padding * 2 + rect.implicitHeight: Math.max(layout.implicitHeight, placeholder.implicitHeight) + root.padding * 2 + content.opacity: 1 + } + } + + transitions: Transition { + Anim { + properties: "x,implicitWidth" + } + Anim { + properties: "y,implicitHeight" + easing: root.Tokens.anim.expressiveFastSpatial + } + Anim { + property: "opacity" + type: Anim.DefaultEffects + } + } + + Behavior on x { + enabled: root.open + + Anim {} + } + + Behavior on y { + enabled: root.open + + Anim {} + } + + Behavior on implicitWidth { + enabled: root.open + + Anim {} + } + + Behavior on implicitHeight { + enabled: root.open + + Anim {} + } + + Item { + id: content + + anchors.fill: parent + clip: true + opacity: 0 + state: Lyrics.loading || !Lyrics.hasLyrics ? "" : "hasLyrics" + + states: State { + name: "hasLyrics" + + PropertyChanges { + layout.opacity: 1 + placeholder.opacity: 0 + } + } + + transitions: [ + Transition { + from: "hasLyrics" + + SequentialAnimation { + Anim { + target: layout + property: "opacity" + type: Anim.FastEffects + } + Anim { + target: placeholder + property: "opacity" + type: Anim.DefaultEffects + } + } + }, + Transition { + to: "hasLyrics" + + SequentialAnimation { + Anim { + target: placeholder + property: "opacity" + type: Anim.FastEffects + } + Anim { + target: layout + property: "opacity" + type: Anim.DefaultEffects + } + } + } + ] + + ColumnLayout { + id: layout + + anchors.centerIn: parent + spacing: Tokens.spacing.extraSmall + opacity: 0 + + StyledText { + text: qsTr("Backend: %1").arg(LyricsBackend.toString(Lyrics.backend)) + color: Colours.palette.m3onSurfaceVariant + animate: true + } + + StyledText { + Layout.maximumWidth: Tokens.sizes.dashboard.mediaTabWidth / 2 + text: qsTr("Selected candidate: %1 | %2 | %3").arg(Lyrics.selectedCandidate.title).arg(Lyrics.selectedCandidate.artist).arg(Lyrics.selectedCandidate.album) + color: Colours.palette.m3onSurfaceVariant + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + animate: true + } + + StyledText { + text: qsTr("Offset: %1 ms").arg(Lyrics.offset) + color: Colours.palette.m3onSurfaceVariant + animate: true + } + } + + Item { + id: placeholder + + anchors.centerIn: parent + implicitWidth: placeholderText.implicitWidth + implicitHeight: placeholderText.implicitHeight + + StyledText { + id: placeholderText + + text: Lyrics.loading ? qsTr("Loading...") : qsTr("No lyrics found") + color: Colours.palette.m3onSurfaceVariant + font: Tokens.font.body.medium + animate: true + } + } + } + } + + MouseArea { + id: btn + + implicitWidth: implicitHeight + implicitHeight: icon.implicitHeight + Tokens.padding.extraSmall * 2 + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + onClicked: root.open = !root.open + + MaterialIcon { + id: icon + + anchors.centerIn: parent + text: "more_vert" + fontStyle: Tokens.font.icon.medium + } + } +}