custom-button: using a proper singleton for the CustomButtonIPCService instead of and object registered in Qt.application. The singleton directly check the settings for IPC calls definition instead of the Bar widgets, this allow us to unload the bar widgets when the bar is hidden while keeping IPC call functional.

This commit is contained in:
Lemmy
2026-02-11 15:22:05 -05:00
parent 02a5d10321
commit b8de6b1599
3 changed files with 193 additions and 105 deletions
+4 -51
View File
@@ -626,60 +626,13 @@ Item {
}
}
// Timer to handle registration attempts
Timer {
id: registrationTimer
interval: 1500
repeat: false
onTriggered: {
// Only register if ipcIdentifier is set
if (ipcIdentifier && ipcIdentifier.trim() !== "") {
// Try to access the service through the global application object
try {
if (typeof Qt !== 'undefined' && Qt.application && Qt.application.customButtonIPCService) {
var service = Qt.application.customButtonIPCService;
var success = service.registerButton(root);
if (success) {
Logger.i("CustomButton", `Successfully registered button with identifier: '${ipcIdentifier}'`);
} else {
Logger.w("CustomButton", `Failed to register button with identifier: '${ipcIdentifier}'`);
}
} else {
Logger.w("CustomButton", `Service not available for button with identifier '${ipcIdentifier}'`);
}
} catch (e) {
Logger.w("CustomButton", `Error during registration of button with identifier '${ipcIdentifier}': ${e.message}`);
}
} else {
Logger.d("CustomButton", `No IPC identifier set for button, skipping registration`);
}
}
}
// Register this button with the IPC service when component is completed
Component.onCompleted: {
registrationTimer.start();
if (ipcIdentifier && ipcIdentifier.trim() !== "")
CustomButtonIPCService.registerButton(root);
}
// Unregister this button when component is destroyed
Component.onDestruction: {
if (ipcIdentifier && ipcIdentifier.trim() !== "") {
// Try to access the service through the global application object for unregistration
try {
if (typeof Qt !== 'undefined' && Qt.application && Qt.application.customButtonIPCService) {
var service = Qt.application.customButtonIPCService;
var success = service.unregisterButton(root);
if (success) {
Logger.i("CustomButton", `Successfully unregistered button with identifier: '${ipcIdentifier}'`);
} else {
Logger.w("CustomButton", `Failed to unregister button with identifier: '${ipcIdentifier}'`);
}
} else {
Logger.w("CustomButton", `Service not available for unregistration of button with identifier '${ipcIdentifier}'`);
}
} catch (e) {
Logger.w("CustomButton", `Error during unregistration of button with identifier '${ipcIdentifier}': ${e.message}`);
}
}
if (ipcIdentifier && ipcIdentifier.trim() !== "")
CustomButtonIPCService.unregisterButton(root);
}
}
+188 -49
View File
@@ -1,25 +1,23 @@
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Commons
import qs.Services.Control
import qs.Services.UI
Item {
Singleton {
id: root
// Must be called at startup to force singleton instantiation,
// which registers the IpcHandler with the IPC system.
function init() {
Logger.i("CustomButtonIPCService", "Service started");
}
// Registry to store references to active custom buttons by their user-defined identifier
property var customButtonRegistry: ({})
Component.onCompleted: {
Logger.i("CustomButtonIPCService", "Service started");
// Make this service globally accessible
if (typeof Qt !== 'undefined' && Qt && Qt.application) {
Qt.application.customButtonIPCService = root;
}
}
// Register a custom button instance
function registerButton(button) {
if (!button || !button.ipcIdentifier) {
@@ -51,6 +49,81 @@ Item {
return customButtonRegistry[identifier] || null;
}
// Find button config from Settings for when the live widget is not loaded
function findButtonConfig(identifier) {
var screens = Quickshell.screens;
for (var i = 0; i < screens.length; i++) {
var widgets = Settings.getBarWidgetsForScreen(screens[i].name);
var config = _searchWidgetsForIdentifier(widgets, identifier);
if (config)
return config;
}
// Also check global widgets as a final fallback
var globalConfig = _searchWidgetsForIdentifier(Settings.data.bar.widgets, identifier);
if (globalConfig)
return globalConfig;
return null;
}
function _searchWidgetsForIdentifier(widgets, identifier) {
var sections = ["left", "center", "right"];
for (var s = 0; s < sections.length; s++) {
var list = widgets[sections[s]];
if (!list)
continue;
for (var j = 0; j < list.length; j++) {
var w = list[j];
if (w.id === "CustomButton" && w.ipcIdentifier === identifier) {
return w;
}
}
}
return null;
}
// Resolve a command property from config with fallback to widgetMetadata defaults
function resolveCommand(config, prop) {
if (config[prop])
return config[prop];
var meta = BarWidgetRegistry.widgetMetadata["CustomButton"];
return meta ? (meta[prop] || "") : "";
}
// Substitute $delta expressions in a command string
function substituteWheelDelta(command, delta) {
var normalizedDelta = delta > 0 ? 1 : -1;
return command.replace(/\$delta([+\-*/]\d+)?/g, function (match, operation) {
if (operation) {
try {
var operator = operation.charAt(0);
var operand = parseInt(operation.substring(1));
var result;
switch (operator) {
case '+':
result = normalizedDelta + operand;
break;
case '-':
result = normalizedDelta - operand;
break;
case '*':
result = normalizedDelta * operand;
break;
case '/':
result = Math.floor(normalizedDelta / operand);
break;
default:
result = normalizedDelta;
}
return result.toString();
} catch (e) {
return normalizedDelta.toString();
}
} else {
return normalizedDelta.toString();
}
});
}
// IpcHandler for custom button commands using short alias 'cb'
IpcHandler {
target: "cb"
@@ -58,15 +131,26 @@ Item {
// Handle left click: cb left "identifier"
function left(identifier: string) {
const button = findButton(identifier);
if (!button) {
Logger.w("CustomButtonIPCService", `Button with identifier '${identifier}' not found`);
if (button) {
if (button.leftClickExec || button.textCommand) {
button.clicked();
Logger.i("CustomButtonIPCService", `Triggered left click on button '${identifier}'`);
} else {
Logger.w("CustomButtonIPCService", `Button '${identifier}' has no left click action configured`);
}
return;
}
// Trigger left click if configured
if (button.leftClickExec || button.textCommand) {
button.clicked();
Logger.i("CustomButtonIPCService", `Triggered left click on button '${identifier}'`);
// Fallback: read from Settings
const config = findButtonConfig(identifier);
if (!config) {
Logger.w("CustomButtonIPCService", `Button with identifier '${identifier}' not found`);
return;
}
const cmd = resolveCommand(config, "leftClickExec");
if (cmd) {
Quickshell.execDetached(["sh", "-lc", cmd]);
Logger.i("CustomButtonIPCService", `Triggered left click on button '${identifier}' (from settings)`);
} else {
Logger.w("CustomButtonIPCService", `Button '${identifier}' has no left click action configured`);
}
@@ -75,15 +159,25 @@ Item {
// Handle right click: cb right "identifier"
function right(identifier: string) {
const button = findButton(identifier);
if (!button) {
Logger.w("CustomButtonIPCService", `Button with identifier '${identifier}' not found`);
if (button) {
if (button.rightClickExec) {
button.rightClicked();
Logger.i("CustomButtonIPCService", `Triggered right click on button '${identifier}'`);
} else {
Logger.w("CustomButtonIPCService", `Button '${identifier}' has no right click action configured`);
}
return;
}
// Trigger right click if configured
if (button.rightClickExec) {
button.rightClicked();
Logger.i("CustomButtonIPCService", `Triggered right click on button '${identifier}'`);
const config = findButtonConfig(identifier);
if (!config) {
Logger.w("CustomButtonIPCService", `Button with identifier '${identifier}' not found`);
return;
}
const cmd = resolveCommand(config, "rightClickExec");
if (cmd) {
Quickshell.execDetached(["sh", "-lc", cmd]);
Logger.i("CustomButtonIPCService", `Triggered right click on button '${identifier}' (from settings)`);
} else {
Logger.w("CustomButtonIPCService", `Button '${identifier}' has no right click action configured`);
}
@@ -92,15 +186,25 @@ Item {
// Handle middle click: cb middle "identifier"
function middle(identifier: string) {
const button = findButton(identifier);
if (!button) {
Logger.w("CustomButtonIPCService", `Button with identifier '${identifier}' not found`);
if (button) {
if (button.middleClickExec) {
button.middleClicked();
Logger.i("CustomButtonIPCService", `Triggered middle click on button '${identifier}'`);
} else {
Logger.w("CustomButtonIPCService", `Button '${identifier}' has no middle click action configured`);
}
return;
}
// Trigger middle click if configured
if (button.middleClickExec) {
button.middleClicked();
Logger.i("CustomButtonIPCService", `Triggered middle click on button '${identifier}'`);
const config = findButtonConfig(identifier);
if (!config) {
Logger.w("CustomButtonIPCService", `Button with identifier '${identifier}' not found`);
return;
}
const cmd = resolveCommand(config, "middleClickExec");
if (cmd) {
Quickshell.execDetached(["sh", "-lc", cmd]);
Logger.i("CustomButtonIPCService", `Triggered middle click on button '${identifier}' (from settings)`);
} else {
Logger.w("CustomButtonIPCService", `Button '${identifier}' has no middle click action configured`);
}
@@ -109,15 +213,27 @@ Item {
// Handle wheel up: cb up "identifier"
function up(identifier: string) {
const button = findButton(identifier);
if (!button) {
Logger.w("CustomButtonIPCService", `Button with identifier '${identifier}' not found`);
if (button) {
if (button.wheelMode === "separate" && button.wheelUpExec) {
button.wheeled(1);
Logger.i("CustomButtonIPCService", `Triggered wheel up on button '${identifier}'`);
} else {
Logger.w("CustomButtonIPCService", `Button '${identifier}' has no separate wheel up action configured or is not in separate mode`);
}
return;
}
// Trigger wheel up if in separate mode and configured
if (button.wheelMode === "separate" && button.wheelUpExec) {
button.wheeled(1);
Logger.i("CustomButtonIPCService", `Triggered wheel up on button '${identifier}'`);
const config = findButtonConfig(identifier);
if (!config) {
Logger.w("CustomButtonIPCService", `Button with identifier '${identifier}' not found`);
return;
}
const mode = config.wheelMode || BarWidgetRegistry.widgetMetadata["CustomButton"].wheelMode;
const cmd = resolveCommand(config, "wheelUpExec");
if (mode === "separate" && cmd) {
const resolved = substituteWheelDelta(cmd, 1);
Quickshell.execDetached(["sh", "-lc", resolved]);
Logger.i("CustomButtonIPCService", `Triggered wheel up on button '${identifier}' (from settings)`);
} else {
Logger.w("CustomButtonIPCService", `Button '${identifier}' has no separate wheel up action configured or is not in separate mode`);
}
@@ -126,15 +242,27 @@ Item {
// Handle wheel down: cb down "identifier"
function down(identifier: string) {
const button = findButton(identifier);
if (!button) {
Logger.w("CustomButtonIPCService", `Button with identifier '${identifier}' not found`);
if (button) {
if (button.wheelMode === "separate" && button.wheelDownExec) {
button.wheeled(-1);
Logger.i("CustomButtonIPCService", `Triggered wheel down on button '${identifier}'`);
} else {
Logger.w("CustomButtonIPCService", `Button '${identifier}' has no separate wheel down action configured or is not in separate mode`);
}
return;
}
// Trigger wheel down if in separate mode and configured
if (button.wheelMode === "separate" && button.wheelDownExec) {
button.wheeled(-1);
Logger.i("CustomButtonIPCService", `Triggered wheel down on button '${identifier}'`);
const config = findButtonConfig(identifier);
if (!config) {
Logger.w("CustomButtonIPCService", `Button with identifier '${identifier}' not found`);
return;
}
const mode = config.wheelMode || BarWidgetRegistry.widgetMetadata["CustomButton"].wheelMode;
const cmd = resolveCommand(config, "wheelDownExec");
if (mode === "separate" && cmd) {
const resolved = substituteWheelDelta(cmd, -1);
Quickshell.execDetached(["sh", "-lc", resolved]);
Logger.i("CustomButtonIPCService", `Triggered wheel down on button '${identifier}' (from settings)`);
} else {
Logger.w("CustomButtonIPCService", `Button '${identifier}' has no separate wheel down action configured or is not in separate mode`);
}
@@ -143,15 +271,27 @@ Item {
// Handle wheel action: cb wheel "identifier"
function wheel(identifier: string) {
const button = findButton(identifier);
if (!button) {
Logger.w("CustomButtonIPCService", `Button with identifier '${identifier}' not found`);
if (button) {
if (button.wheelMode === "unified" && button.wheelExec) {
button.wheeled(1);
Logger.i("CustomButtonIPCService", `Triggered wheel action on button '${identifier}'`);
} else {
Logger.w("CustomButtonIPCService", `Button '${identifier}' has no unified wheel action configured or is not in unified mode`);
}
return;
}
// Trigger unified wheel if in unified mode and configured
if (button.wheelMode === "unified" && button.wheelExec) {
button.wheeled(1);
Logger.i("CustomButtonIPCService", `Triggered wheel action on button '${identifier}'`);
const config = findButtonConfig(identifier);
if (!config) {
Logger.w("CustomButtonIPCService", `Button with identifier '${identifier}' not found`);
return;
}
const mode = config.wheelMode || BarWidgetRegistry.widgetMetadata["CustomButton"].wheelMode;
const cmd = resolveCommand(config, "wheelExec");
if (mode === "unified" && cmd) {
const resolved = substituteWheelDelta(cmd, 1);
Quickshell.execDetached(["sh", "-lc", resolved]);
Logger.i("CustomButtonIPCService", `Triggered wheel action on button '${identifier}' (from settings)`);
} else {
Logger.w("CustomButtonIPCService", `Button '${identifier}' has no unified wheel action configured or is not in unified mode`);
}
@@ -161,11 +301,10 @@ Item {
function refresh(identifier: string) {
const button = findButton(identifier);
if (!button) {
Logger.w("CustomButtonIPCService", `Button with identifier '${identifier}' not found`);
Logger.w("CustomButtonIPCService", `Button '${identifier}' is not currently loaded refresh requires a live widget instance`);
return;
}
// Trigger text command refresh if configured and not streaming
if (button.textCommand && button.textCommand.length > 0 && !button.textStream) {
button.runTextCommand();
Logger.i("CustomButtonIPCService", `Triggered refresh (text command) on button '${identifier}'`);
+1 -5
View File
@@ -109,6 +109,7 @@ ShellRoot {
HostService.init();
GitHubService.init();
SupporterService.init();
CustomButtonIPCService.init();
});
delayedInitTimer.running = true;
@@ -147,11 +148,6 @@ ShellRoot {
screenDetector: screenDetector
}
// CustomButtonIPCService handles IPC commands for custom buttons
CustomButtonIPCService {
id: customButtonIPCService
}
// Container for plugins Main.qml instances (must be in graphics scene)
Item {
id: pluginContainer