Add audio visualizer to LockScreen
This commit is contained in:
parent
7c733b9f47
commit
6771248d29
4 changed files with 145 additions and 3 deletions
|
|
@ -11,6 +11,7 @@ import Quickshell.Widgets
|
|||
import qs.Commons
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.Audio
|
||||
|
||||
Loader {
|
||||
id: lockScreen
|
||||
|
|
@ -294,7 +295,7 @@ Loader {
|
|||
spacing: Style.marginM * scaling
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
// Animated avatar with glow effect
|
||||
// Animated avatar with glow effect or audio visualizer
|
||||
Rectangle {
|
||||
width: 120 * scaling
|
||||
height: 120 * scaling
|
||||
|
|
@ -304,7 +305,140 @@ Loader {
|
|||
border.width: Math.max(1, Style.borderL * scaling)
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
// Glow effect
|
||||
// Circular audio visualizer when music is playing
|
||||
Loader {
|
||||
active: MediaService.isPlaying && Settings.data.audio.visualizerType == "linear"
|
||||
anchors.centerIn: parent
|
||||
width: 160 * scaling
|
||||
height: 160 * scaling
|
||||
|
||||
sourceComponent: Item {
|
||||
Repeater {
|
||||
model: CavaService.values.length
|
||||
|
||||
Rectangle {
|
||||
property real linearAngle: (index / CavaService.values.length) * 2 * Math.PI
|
||||
property real linearRadius: 70 * scaling
|
||||
property real linearBarLength: Math.max(2, CavaService.values[index] * 30 * scaling)
|
||||
property real linearBarWidth: 3 * scaling
|
||||
|
||||
width: linearBarWidth
|
||||
height: linearBarLength
|
||||
color: Color.mPrimary
|
||||
radius: linearBarWidth * 0.5
|
||||
|
||||
x: parent.width * 0.5 + Math.cos(linearAngle) * linearRadius - width * 0.5
|
||||
y: parent.height * 0.5 + Math.sin(linearAngle) * linearRadius - height * 0.5
|
||||
|
||||
transform: Rotation {
|
||||
origin.x: linearBarWidth * 0.5
|
||||
origin.y: linearBarLength * 0.5
|
||||
angle: (linearAngle * 180 / Math.PI) + 90
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: MediaService.isPlaying && Settings.data.audio.visualizerType == "mirrored"
|
||||
anchors.centerIn: parent
|
||||
width: 160 * scaling
|
||||
height: 160 * scaling
|
||||
|
||||
sourceComponent: Item {
|
||||
Repeater {
|
||||
model: CavaService.values.length * 2
|
||||
|
||||
Rectangle {
|
||||
property int mirroredValueIndex: index < CavaService.values.length ? index : (CavaService.values.length * 2 - 1 - index)
|
||||
property real mirroredAngle: (index / (CavaService.values.length * 2)) * 2 * Math.PI
|
||||
property real mirroredRadius: 70 * scaling
|
||||
property real mirroredBarLength: Math.max(2, CavaService.values[mirroredValueIndex] * 30 * scaling)
|
||||
property real mirroredBarWidth: 3 * scaling
|
||||
|
||||
width: mirroredBarWidth
|
||||
height: mirroredBarLength
|
||||
color: Color.mPrimary
|
||||
radius: mirroredBarWidth * 0.5
|
||||
|
||||
x: parent.width * 0.5 + Math.cos(mirroredAngle) * mirroredRadius - width * 0.5
|
||||
y: parent.height * 0.5 + Math.sin(mirroredAngle) * mirroredRadius - height * 0.5
|
||||
|
||||
transform: Rotation {
|
||||
origin.x: mirroredBarWidth * 0.5
|
||||
origin.y: mirroredBarLength * 0.5
|
||||
angle: (mirroredAngle * 180 / Math.PI) + 90
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: MediaService.isPlaying && Settings.data.audio.visualizerType == "wave"
|
||||
anchors.centerIn: parent
|
||||
width: 160 * scaling
|
||||
height: 160 * scaling
|
||||
|
||||
sourceComponent: Item {
|
||||
Canvas {
|
||||
id: waveCanvas
|
||||
anchors.fill: parent
|
||||
antialiasing: true
|
||||
|
||||
onPaint: {
|
||||
var ctx = getContext("2d")
|
||||
ctx.reset()
|
||||
|
||||
if (CavaService.values.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.strokeStyle = Color.mPrimary
|
||||
ctx.lineWidth = 2 * scaling
|
||||
ctx.lineCap = "round"
|
||||
|
||||
var centerX = width * 0.5
|
||||
var centerY = height * 0.5
|
||||
var baseRadius = 60 * scaling
|
||||
var maxAmplitude = 20 * scaling
|
||||
|
||||
ctx.beginPath()
|
||||
|
||||
for (var i = 0; i <= CavaService.values.length; i++) {
|
||||
var index = i % CavaService.values.length
|
||||
var angle = (i / CavaService.values.length) * 2 * Math.PI
|
||||
var amplitude = CavaService.values[index] * maxAmplitude
|
||||
var radius = baseRadius + amplitude
|
||||
|
||||
var x = centerX + Math.cos(angle) * radius
|
||||
var y = centerY + Math.sin(angle) * radius
|
||||
|
||||
if (i === 0) {
|
||||
ctx.moveTo(x, y)
|
||||
} else {
|
||||
ctx.lineTo(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
ctx.closePath()
|
||||
ctx.stroke()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 16 // ~60 FPS
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
waveCanvas.requestPaint()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Glow effect when no music is playing
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width + 24 * scaling
|
||||
|
|
@ -314,6 +448,7 @@ Loader {
|
|||
border.color: Qt.rgba(Color.mPrimary.r, Color.mPrimary.g, Color.mPrimary.b, 0.3)
|
||||
border.width: Math.max(1, Style.borderM * scaling)
|
||||
z: -1
|
||||
visible: !MediaService.isPlaying
|
||||
|
||||
SequentialAnimation on scale {
|
||||
loops: Animation.Infinite
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@ Singleton {
|
|||
id: process
|
||||
stdinEnabled: true
|
||||
running: (Settings.data.audio.visualizerType !== "none") && (PanelService.sidePanel.active
|
||||
|| Settings.data.audio.showMiniplayerCava)
|
||||
|| Settings.data.audio.showMiniplayerCava
|
||||
|| (PanelService.lockScreen && PanelService.lockScreen.active))
|
||||
command: ["cava", "-p", "/dev/stdin"]
|
||||
onExited: {
|
||||
stdinEnabled = true
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ Singleton {
|
|||
// A ref. to the sidePanel, so it's accessible from other services
|
||||
property var sidePanel: null
|
||||
|
||||
// A ref. to the lockScreen, so it's accessible from other services
|
||||
property var lockScreen: null
|
||||
|
||||
// Currently opened panel
|
||||
property var openedPanel: null
|
||||
|
||||
|
|
|
|||
|
|
@ -78,6 +78,9 @@ ShellRoot {
|
|||
// Save a ref. to our sidePanel so we can access it from services
|
||||
PanelService.sidePanel = sidePanel
|
||||
|
||||
// Save a ref. to our lockScreen so we can access it from services
|
||||
PanelService.lockScreen = lockScreen
|
||||
|
||||
// Ensure our singleton is created as soon as possible so we start fetching weather asap
|
||||
LocationService.init()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue