From 8a4ca8927f4c95b2016e6e39b9ebca030701e1de Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Fri, 31 Oct 2025 22:52:53 -0400 Subject: [PATCH] Bar-AudioVisualizer: vertical bar support --- Modules/Audio/LinearSpectrum.qml | 11 ++-- Modules/Audio/MirroredSpectrum.qml | 14 +++-- Modules/Audio/WaveSpectrum.qml | 79 ++++++++++++++++++------- Modules/Bar/Widgets/AudioVisualizer.qml | 12 ++-- 4 files changed, 78 insertions(+), 38 deletions(-) diff --git a/Modules/Audio/LinearSpectrum.qml b/Modules/Audio/LinearSpectrum.qml index fd784037..a0bbd34c 100644 --- a/Modules/Audio/LinearSpectrum.qml +++ b/Modules/Audio/LinearSpectrum.qml @@ -7,6 +7,7 @@ Item { property color strokeColor: Color.mOnSurface property int strokeWidth: 0 property var values: [] + property bool vertical: false // Minimum signal properties property bool showMinimumSignal: false @@ -15,7 +16,7 @@ Item { // Pre compute horizontal mirroring readonly property int valuesCount: values.length readonly property int totalBars: valuesCount * 2 - readonly property real barSlotWidth: totalBars > 0 ? width / totalBars : 0 + readonly property real barSlotSize: totalBars > 0 ? (vertical ? height : width) / totalBars : 0 Repeater { model: root.totalBars @@ -34,10 +35,10 @@ Item { border.width: root.strokeWidth antialiasing: true - width: root.barSlotWidth * 0.5 // Creates a small gap between bars - height: root.height * amp - x: index * root.barSlotWidth + (root.barSlotWidth * 0.25) - y: root.height - height + width: vertical ? root.width * amp : root.barSlotSize * 0.5 + height: vertical ? root.barSlotSize * 0.5 : root.height * amp + x: vertical ? root.width - width : index * root.barSlotSize + (root.barSlotSize * 0.25) + y: vertical ? index * root.barSlotSize + (root.barSlotSize * 0.25) : root.height - height } } } diff --git a/Modules/Audio/MirroredSpectrum.qml b/Modules/Audio/MirroredSpectrum.qml index de646910..dd5c98ee 100644 --- a/Modules/Audio/MirroredSpectrum.qml +++ b/Modules/Audio/MirroredSpectrum.qml @@ -7,6 +7,7 @@ Item { property color strokeColor: Color.mOnSurface property int strokeWidth: 0 property var values: [] + property bool vertical: false // Minimum signal properties property bool showMinimumSignal: false @@ -15,9 +16,10 @@ Item { // Pre-compute mirroring readonly property int valuesCount: values.length readonly property int totalBars: valuesCount * 2 - readonly property real barSlotWidth: totalBars > 0 ? width / totalBars : 0 + readonly property real barSlotSize: totalBars > 0 ? (vertical ? height : width) / totalBars : 0 readonly property real centerY: height / 2 + readonly property real centerX: width / 2 Repeater { model: root.totalBars @@ -31,17 +33,17 @@ Item { property real rawAmp: root.values[valueIndex] property real amp: (root.showMinimumSignal && rawAmp === 0) ? root.minimumSignalValue : rawAmp - property real barHeight: root.height * amp + property real barSize: (vertical ? root.width : root.height) * amp color: root.fillColor border.color: root.strokeColor border.width: root.strokeWidth antialiasing: true - width: root.barSlotWidth * 0.8 // Creates a small gap between bars - height: barHeight - x: index * root.barSlotWidth + (root.barSlotWidth * 0.25) - y: root.centerY - (barHeight / 2) + width: vertical ? barSize : root.barSlotSize * 0.8 + height: vertical ? root.barSlotSize * 0.8 : barSize + x: vertical ? root.centerX - (barSize / 2) : index * root.barSlotSize + (root.barSlotSize * 0.25) + y: vertical ? index * root.barSlotSize + (root.barSlotSize * 0.25) : root.centerY - (barSize / 2) } } } diff --git a/Modules/Audio/WaveSpectrum.qml b/Modules/Audio/WaveSpectrum.qml index 912d7456..556726b4 100644 --- a/Modules/Audio/WaveSpectrum.qml +++ b/Modules/Audio/WaveSpectrum.qml @@ -7,6 +7,7 @@ Item { property color strokeColor: Color.mOnSurface property int strokeWidth: 0 property var values: [] + property bool vertical: false // Minimum signal properties property bool showMinimumSignal: false @@ -20,6 +21,7 @@ Item { onStrokeColorChanged: canvas.requestPaint() onShowMinimumSignalChanged: canvas.requestPaint() onMinimumSignalValueChanged: canvas.requestPaint() + onVerticalChanged: canvas.requestPaint() Canvas { id: canvas @@ -52,34 +54,65 @@ Item { ctx.lineWidth = root.strokeWidth const count = mirroredValues.length - const stepX = width / (count - 1) - const centerY = height / 2 - const amplitude = height / 2 - ctx.beginPath() + if (root.vertical) { + // Vertical orientation + const stepY = height / (count - 1) + const centerX = width / 2 + const amplitude = width / 2 - // Draw the top half of the waveform from left to right - // Use the calculated offset for the first point - var yOffset = mirroredValues[0] * amplitude - ctx.moveTo(0, centerY - yOffset) + ctx.beginPath() - for (var i = 1; i < count; i++) { - const x = i * stepX - yOffset = mirroredValues[i] * amplitude - const y = centerY - yOffset - ctx.lineTo(x, y) + // Draw the left half of the waveform from top to bottom + var xOffset = mirroredValues[0] * amplitude + ctx.moveTo(centerX - xOffset, 0) + + for (var i = 1; i < count; i++) { + const y = i * stepY + xOffset = mirroredValues[i] * amplitude + const x = centerX - xOffset + ctx.lineTo(x, y) + } + + // Draw the right half of the waveform from bottom to top to create a closed shape + for (var i = count - 1; i >= 0; i--) { + const y = i * stepY + xOffset = mirroredValues[i] * amplitude + const x = centerX + xOffset // Mirrored across the center + ctx.lineTo(x, y) + } + + ctx.closePath() + } else { + // Horizontal orientation + const stepX = width / (count - 1) + const centerY = height / 2 + const amplitude = height / 2 + + ctx.beginPath() + + // Draw the top half of the waveform from left to right + var yOffset = mirroredValues[0] * amplitude + ctx.moveTo(0, centerY - yOffset) + + for (var i = 1; i < count; i++) { + const x = i * stepX + yOffset = mirroredValues[i] * amplitude + const y = centerY - yOffset + ctx.lineTo(x, y) + } + + // Draw the bottom half of the waveform from right to left to create a closed shape + for (var i = count - 1; i >= 0; i--) { + const x = i * stepX + yOffset = mirroredValues[i] * amplitude + const y = centerY + yOffset // Mirrored across the center + ctx.lineTo(x, y) + } + + ctx.closePath() } - // Draw the bottom half of the waveform from right to left to create a closed shape - for (var i = count - 1; i >= 0; i--) { - const x = i * stepX - yOffset = mirroredValues[i] * amplitude - const y = centerY + yOffset // Mirrored across the center - ctx.lineTo(x, y) - } - - ctx.closePath() - // --- Render the path --- if (root.fillColor.a > 0) { ctx.fill() diff --git a/Modules/Bar/Widgets/AudioVisualizer.qml b/Modules/Bar/Widgets/AudioVisualizer.qml index 1d8c9ee3..cc8fb4d5 100644 --- a/Modules/Bar/Widgets/AudioVisualizer.qml +++ b/Modules/Bar/Widgets/AudioVisualizer.qml @@ -16,6 +16,8 @@ Item { property int sectionWidgetIndex: -1 property int sectionWidgetsCount: 0 + readonly property bool isVerticalBar: (Settings.data.bar.position === "left" || Settings.data.bar.position === "right") + property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId] property var widgetSettings: { if (section && sectionWidgetIndex >= 0) { @@ -50,8 +52,8 @@ Item { readonly property bool shouldShow: (currentVisualizerType !== "" && currentVisualizerType !== "none") && (!hideWhenIdle || MediaService.isPlaying) - implicitWidth: shouldShow ? visualizerWidth : 0 - implicitHeight: shouldShow ? Style.capsuleHeight : 0 + implicitWidth: !shouldShow ? 0 : isVerticalBar ? Style.capsuleHeight : visualizerWidth + implicitHeight: !shouldShow ? 0 : isVerticalBar ? visualizerWidth : Style.capsuleHeight visible: shouldShow Behavior on implicitWidth { @@ -78,14 +80,13 @@ Item { readonly property string currentVisualizerType: Settings.data.audio.visualizerType // When visualizer type or playback changes, shouldShow updates automatically - // The Loader dynamically loads the appropriate visualizer based on settings Loader { id: visualizerLoader anchors.fill: parent anchors.margins: Style.marginS active: shouldShow - asynchronous: false + asynchronous: true sourceComponent: { switch (currentVisualizerType) { @@ -128,6 +129,7 @@ Item { values: CavaService.values fillColor: root.fillColor showMinimumSignal: true + vertical: root.isVerticalBar } } @@ -138,6 +140,7 @@ Item { values: CavaService.values fillColor: root.fillColor showMinimumSignal: true + vertical: root.isVerticalBar } } @@ -148,6 +151,7 @@ Item { values: CavaService.values fillColor: root.fillColor showMinimumSignal: true + vertical: root.isVerticalBar } } }