Cava: refactored the way we enable/disable it dynamically so it can work with plugins, desktop widgets, etc...

This commit is contained in:
Lemmy
2025-12-20 21:07:20 -05:00
parent 9e4d60e504
commit 8cd17f77cb
7 changed files with 120 additions and 55 deletions
+17
View File
@@ -55,6 +55,23 @@ Item {
readonly property bool shouldShow: (currentVisualizerType !== "" && currentVisualizerType !== "none") && (!hideWhenIdle || MediaService.isPlaying)
// Register/unregister with CavaService based on visibility
readonly property string cavaComponentId: "bar:audiovisualizer:" + root.screen.name + ":" + root.section + ":" + root.sectionWidgetIndex
onShouldShowChanged: {
if (root.shouldShow) {
CavaService.registerComponent(root.cavaComponentId);
} else {
CavaService.unregisterComponent(root.cavaComponentId);
}
}
Component.onDestruction: {
if (root.shouldShow) {
CavaService.unregisterComponent(root.cavaComponentId);
}
}
implicitWidth: !shouldShow ? 0 : isVerticalBar ? Style.capsuleHeight : visualizerWidth
implicitHeight: !shouldShow ? 0 : isVerticalBar ? visualizerWidth : Style.capsuleHeight
visible: shouldShow
+18
View File
@@ -66,6 +66,24 @@ Item {
return showArtistFirst ? (artist ? `${artist} - ${track}` : track) : (artist ? `${track} - ${artist}` : track);
}
// CavaService registration for visualizer
readonly property string cavaComponentId: "bar:mediamini:" + root.screen.name + ":" + root.section + ":" + root.sectionWidgetIndex
readonly property bool needsCava: root.showVisualizer && root.visualizerType !== "" && root.visualizerType !== "none"
onneedsCavaChanged: {
if (root.needsCava) {
CavaService.registerComponent(root.cavaComponentId);
} else {
CavaService.unregisterComponent(root.cavaComponentId);
}
}
Component.onDestruction: {
if (root.needsCava) {
CavaService.unregisterComponent(root.cavaComponentId);
}
}
readonly property string tooltipText: {
var text = title;
var controls = [];
+21
View File
@@ -15,6 +15,27 @@ NBox {
// Track whether we have an active media player
readonly property bool hasActivePlayer: MediaService.currentPlayer && MediaService.canPlay
// CavaService registration for visualizer
readonly property bool needsCava: Settings.data.audio.visualizerType !== "" && Settings.data.audio.visualizerType !== "none"
onNeedsCavaChanged: {
if (root.needsCava) {
CavaService.registerComponent("mediacard");
} else {
CavaService.unregisterComponent("mediacard");
}
}
Component.onCompleted: {
if (root.needsCava) {
CavaService.registerComponent("mediacard");
}
}
Component.onDestruction: {
CavaService.unregisterComponent("mediacard");
}
property string wallpaper: WallpaperService.getWallpaper(screen.name)
// External state management
@@ -24,6 +24,27 @@ DraggableDesktopWidget {
readonly property bool isHidden: (shouldHideIdle || shouldHideEmpty) && !Settings.data.desktopWidgets.editMode
visible: !isHidden
// CavaService registration for visualizer
readonly property string cavaComponentId: "desktopmediaplayer:" + (widgetData.id || "default")
onShouldShowVisualizerChanged: {
if (root.shouldShowVisualizer) {
CavaService.registerComponent(root.cavaComponentId);
} else {
CavaService.unregisterComponent(root.cavaComponentId);
}
}
Component.onCompleted: {
if (root.shouldShowVisualizer) {
CavaService.registerComponent(root.cavaComponentId);
}
}
Component.onDestruction: {
CavaService.unregisterComponent(root.cavaComponentId);
}
readonly property bool showPrev: hasPlayer && MediaService.canGoPrevious
readonly property bool showNext: hasPlayer && MediaService.canGoNext
readonly property int visibleButtonCount: 1 + (showPrev ? 1 : 0) + (showNext ? 1 : 0)
+19
View File
@@ -24,6 +24,25 @@ Loader {
id: root
active: false
// Track if the visualizer should be shown (lockscreen active + media playing + non-compact mode)
readonly property bool needsCava: root.active && !Settings.data.general.compactLockScreen && Settings.data.audio.visualizerType !== "" && Settings.data.audio.visualizerType !== "none"
onActiveChanged: {
if (root.active && root.needsCava) {
CavaService.registerComponent("lockscreen");
} else {
CavaService.unregisterComponent("lockscreen");
}
}
onNeedsCavaChanged: {
if (root.needsCava) {
CavaService.registerComponent("lockscreen");
} else {
CavaService.unregisterComponent("lockscreen");
}
}
Component.onCompleted: {
// Register with panel service
PanelService.lockScreen = this;
+24 -25
View File
@@ -9,31 +9,30 @@ import qs.Services.UI
Singleton {
id: root
/**
* Cava runs if:
* - Bar has an audio visualizer
* - LockScreen is opened
* - A control center is open
* - Desktop media player has a visualizer enabled
*/
readonly property bool hasDesktopMediaVisualizer: (function () {
var monitorWidgets = Settings.data.desktopWidgets.monitorWidgets;
if (!monitorWidgets)
return false;
for (var i = 0; i < monitorWidgets.length; i++) {
var widgets = monitorWidgets[i].widgets;
if (!widgets)
continue;
for (var j = 0; j < widgets.length; j++) {
var widget = widgets[j];
if (widget.id === "MediaPlayer" && widget.visualizerType && widget.visualizerType !== "" && widget.visualizerType !== "none") {
return true;
}
}
}
return false;
})()
property bool shouldRun: BarService.hasAudioVisualizer || PanelService.lockScreen?.active || (PanelService.openedPanel && PanelService.openedPanel.objectName.startsWith("controlCenterPanel")) || hasDesktopMediaVisualizer
// Register a component that needs audio data, call this when a visualizer becomes active.
// Pass a unique identifier (e.g., "lockscreen", "controlcenter:screen1", "plugin:fancy-audiovisualizer")
function registerComponent(componentId) {
root._registeredComponents[componentId] = true;
root._registeredComponents = Object.assign({}, root._registeredComponents);
Logger.d("Cava", "Component registered:", componentId, "- total:", root.registeredCount);
}
// Unregister a component when it no longer needs audio data.
function unregisterComponent(componentId) {
delete root._registeredComponents[componentId];
root._registeredComponents = Object.assign({}, root._registeredComponents);
Logger.d("Cava", "Component unregistered:", componentId, "- total:", root.registeredCount);
}
// Check if a component is registered
function isRegistered(componentId) {
return root._registeredComponents[componentId] === true;
}
// Component registration - any component needing audio data registers here
property var _registeredComponents: ({})
readonly property int registeredCount: Object.keys(_registeredComponents).length
property bool shouldRun: registeredCount > 0
property var values: []
property int barsCount: 32
-30
View File
@@ -7,7 +7,6 @@ import qs.Commons
Singleton {
id: root
property bool hasAudioVisualizer: false
property bool isVisible: true
property var readyBars: ({})
@@ -18,33 +17,6 @@ Singleton {
signal activeWidgetsChanged
signal barReadyChanged(string screenName)
// onHasAudioVisualizerChanged: {
// Logger.d("BarService", "hasAudioVisualizer", hasAudioVisualizer)
// }
// Simple timer that run once when the widget structure has changed
// and determine if any MediaMini widget has the visualizer on
Timer {
id: timerCheckVisualizer
interval: 100
repeat: false
onTriggered: {
hasAudioVisualizer = false;
if (getAllWidgetInstances("AudioVisualizer").length > 0) {
hasAudioVisualizer = true;
return;
}
const widgets = getAllWidgetInstances("MediaMini");
for (var i = 0; i < widgets.length; i++) {
const widget = widgets[i];
if (widget.showVisualizer) {
hasAudioVisualizer = true;
return;
}
}
}
}
Component.onCompleted: {
Logger.i("BarService", "Service started");
}
@@ -75,8 +47,6 @@ Singleton {
"instance": instance
};
timerCheckVisualizer.restart();
Logger.d("BarService", "Registered widget:", key);
root.activeWidgetsChanged();
}