nosignal-shell/modules/dashboard/media/CoverVisualiser.qml
2026-05-04 03:47:03 +10:00

84 lines
2.6 KiB
QML

pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Shapes
import Quickshell
import Caelestia.Config
import Caelestia.Services
import qs.components
import qs.components.widgets
import qs.services
Item {
id: root
readonly property real centerX: width / 2
readonly property real centerY: height / 2
readonly property real coverMaxRadius: {
const bounds = cover.shape.pathBounds();
return Math.max(bounds.width, bounds.height) / 2;
}
readonly property real spacing: Tokens.spacing.medium
readonly property real maxMagnitude: (implicitWidth - cover.implicitWidth) / 2 - spacing
ServiceRef {
service: Audio.cava
}
Shape {
anchors.fill: parent
asynchronous: true
preferredRendererType: Shape.CurveRenderer
data: bars.instances
}
Variants {
id: bars
model: Array.from({
length: GlobalConfig.services.visualiserBars
}, (_, i) => i)
ShapePath {
id: bar
required property int modelData
readonly property real value: Math.max(1e-3, Math.min(1, Audio.cava.values[modelData]))
readonly property real angle: modelData * 2 * Math.PI / GlobalConfig.services.visualiserBars
readonly property real dist: root.coverMaxRadius + root.spacing + strokeWidth / 2 + value * root.maxMagnitude
readonly property real shapeEdgeDist: {
cover.shape.rotation; // Update when shape rotation changes
const sDist = cover.shape.distanceAtAngle(modelData * 360 / GlobalConfig.services.visualiserBars);
return sDist + root.spacing + strokeWidth / 2;
}
readonly property real cos: Math.cos(angle)
readonly property real sin: Math.sin(angle)
asynchronous: true
capStyle: root.Tokens.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
strokeWidth: 360 / GlobalConfig.services.visualiserBars - root.Tokens.spacing.small / 4
strokeColor: Colours.palette.m3primary
startX: root.centerX + shapeEdgeDist * cos
startY: root.centerY + shapeEdgeDist * sin
PathLine {
x: root.centerX + bar.dist * bar.cos
y: root.centerY + bar.dist * bar.sin
}
Behavior on strokeColor {
CAnim {}
}
}
}
CoverArt {
id: cover
anchors.centerIn: parent
implicitWidth: Tokens.sizes.dashboard.mediaCoverArtSize
implicitHeight: Tokens.sizes.dashboard.mediaCoverArtSize
}
}