diff --git a/Modules/Bar/Widgets/AudioVisualizer.qml b/Modules/Bar/Widgets/AudioVisualizer.qml new file mode 100644 index 00000000..a91cce70 --- /dev/null +++ b/Modules/Bar/Widgets/AudioVisualizer.qml @@ -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 + } + } +} diff --git a/Modules/Settings/Bar/BarWidgetSettingsDialog.qml b/Modules/Settings/Bar/BarWidgetSettingsDialog.qml index ba200384..bfb72b9c 100644 --- a/Modules/Settings/Bar/BarWidgetSettingsDialog.qml +++ b/Modules/Settings/Bar/BarWidgetSettingsDialog.qml @@ -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", diff --git a/Modules/Settings/Bar/WidgetSettings/AudioVisualizerSettings.qml b/Modules/Settings/Bar/WidgetSettings/AudioVisualizerSettings.qml new file mode 100644 index 00000000..768ce805 --- /dev/null +++ b/Modules/Settings/Bar/WidgetSettings/AudioVisualizerSettings.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") + } +} diff --git a/Services/BarWidgetRegistry.qml b/Services/BarWidgetRegistry.qml index 4e8b5621..cc0f37e4 100644 --- a/Services/BarWidgetRegistry.qml +++ b/Services/BarWidgetRegistry.qml @@ -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 {} }