AudioVisualizer: basic implementation

This commit is contained in:
ItsLemmy
2025-10-26 22:30:20 -04:00
parent 2c85dfd5cd
commit 178d18eca9
4 changed files with 179 additions and 0 deletions
+140
View File
@@ -0,0 +1,140 @@
import QtQuick
import Quickshell
import qs.Commons
import qs.Modules.Audio
import qs.Services
import qs.Widgets
Item {
id: root
property ShellScreen screen
// Widget properties passed from Bar.qml for per-instance settings
property string widgetId: ""
property string section: ""
property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
property var widgetSettings: {
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.data.bar.widgets[section]
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex]
}
}
return {}
}
// Resolve settings: try user settings or defaults from BarWidgetRegistry
readonly property int visualizerWidth: widgetSettings.width !== undefined ? widgetSettings.width : widgetMetadata.width
implicitWidth: visualizerWidth
implicitHeight: Style.capsuleHeight
Rectangle {
id: background
anchors.fill: parent
radius: Style.radiusS
color: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent
}
// Store visualizer type to force re-evaluation
readonly property string currentVisualizerType: Settings.data.audio.visualizerType
// Timer to delay reload
Timer {
id: reloadTimer
interval: 50
onTriggered: {
visualizerLoader.active = true
}
}
// Force reload when visualizer type changes
onCurrentVisualizerTypeChanged: {
visualizerLoader.active = false
reloadTimer.restart()
}
// The Loader dynamically loads the appropriate visualizer based on settings
Loader {
id: visualizerLoader
anchors.fill: parent
anchors.margins: Style.marginS
active: false
asynchronous: false
sourceComponent: {
switch (currentVisualizerType) {
case "linear":
return linearComponent
case "mirrored":
return mirroredComponent
case "wave":
return waveComponent
default:
return null
}
}
}
// Click to cycle through visualizer types
MouseArea {
id: mouseArea
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
acceptedButtons: Qt.LeftButton
onClicked: mouse => {
console.log("CavaVisualizer clicked! Current type:", currentVisualizerType)
const types = ["linear", "mirrored", "wave"]
const currentIndex = types.indexOf(currentVisualizerType)
const nextIndex = (currentIndex + 1) % types.length
const newType = types[nextIndex]
console.log("Switching from", currentVisualizerType, "to", newType)
// Update settings directly
Settings.data.audio.visualizerType = newType
console.log("Settings saved")
}
}
// Initial activation on component complete
Component.onCompleted: {
if (currentVisualizerType !== "" && currentVisualizerType !== "none") {
visualizerLoader.active = true
}
}
Component {
id: linearComponent
LinearSpectrum {
anchors.fill: parent
values: CavaService.values
fillColor: Color.mPrimary
}
}
Component {
id: mirroredComponent
MirroredSpectrum {
anchors.fill: parent
values: CavaService.values
fillColor: Color.mPrimary
}
}
Component {
id: waveComponent
WaveSpectrum {
anchors.fill: parent
values: CavaService.values
fillColor: Color.mPrimary
}
}
}
@@ -123,6 +123,7 @@ Popup {
function loadWidgetSettings() {
const widgetSettingsMap = {
"ActiveWindow": "WidgetSettings/ActiveWindowSettings.qml",
"AudioVisualizer": "WidgetSettings/AudioVisualizerSettings.qml",
"Battery": "WidgetSettings/BatterySettings.qml",
"Bluetooth": "WidgetSettings/BluetoothSettings.qml",
"Brightness": "WidgetSettings/BrightnessSettings.qml",
@@ -0,0 +1,30 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
import qs.Services
ColumnLayout {
id: root
spacing: Style.marginM
// Properties to receive data from parent
property var widgetData: null
property var widgetMetadata: null
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.width = parseInt(widthInput.text) || widgetMetadata.width
return settings
}
NTextInput {
id: widthInput
Layout.fillWidth: true
label: I18n.tr("bar.widget-settings.audio-visualizer.width.label")
description: I18n.tr("bar.widget-settings.audio-visualizer.width.description")
text: widgetData.width || widgetMetadata.width
placeholderText: I18n.tr("placeholders.enter-width-pixels")
}
}
+8
View File
@@ -11,6 +11,7 @@ Singleton {
// Widget registry object mapping widget names to components
property var widgets: ({
"ActiveWindow": activeWindowComponent,
"AudioVisualizer": audioVisualizerComponent,
"Battery": batteryComponent,
"Bluetooth": bluetoothComponent,
"Brightness": brightnessComponent,
@@ -49,6 +50,10 @@ Singleton {
"useFixedWidth": false,
"colorizeIcons": false
},
"AudioVisualizer": {
"allowUserSettings": true,
"width": 200
},
"Battery": {
"allowUserSettings": true,
"displayMode": "onhover",
@@ -159,6 +164,9 @@ Singleton {
property Component activeWindowComponent: Component {
ActiveWindow {}
}
property Component audioVisualizerComponent: Component {
AudioVisualizer {}
}
property Component batteryComponent: Component {
Battery {}
}