import QtQuick import Quickshell import Quickshell.Io import qs.Commons import qs.Services import qs.Widgets Item { id: root property string widgetId: "CustomButton" property var widgetSettings: null property string onClickedCommand: "" property string onRightClickedCommand: "" property string onMiddleClickedCommand: "" property string stateChecksJson: "[]" // Store state checks as JSON string property string generalTooltipText: "Custom Button" property bool enableOnStateLogic: false property string _currentIcon: "heart" // Default icon property bool _isHot: false property var _parsedStateChecks: [] // Local array for parsed state checks Connections { target: root function _updatePropertiesFromSettings() { if (!widgetSettings) { return } onClickedCommand = widgetSettings.onClicked || "" onRightClickedCommand = widgetSettings.onRightClicked || "" onMiddleClickedCommand = widgetSettings.onMiddleClicked || "" stateChecksJson = widgetSettings.stateChecksJson || "[]" // Populate from widgetSettings try { _parsedStateChecks = JSON.parse(stateChecksJson) } catch (e) { console.error("CustomButton: Failed to parse stateChecksJson:", e.message) _parsedStateChecks = [] } generalTooltipText = widgetSettings.generalTooltipText || "Custom Button" enableOnStateLogic = widgetSettings.enableOnStateLogic || false updateState() } function onWidgetSettingsChanged() { if (widgetSettings) { _updatePropertiesFromSettings() } } } property int _currentStateCheckIndex: -1 property string _activeStateIcon: "" Process { id: stateCheckProcessExecutor running: false command: _currentStateCheckIndex !== -1 && _parsedStateChecks.length > _currentStateCheckIndex ? ["sh", "-c", _parsedStateChecks[_currentStateCheckIndex].command] : [] onExited: function(exitCode, stdout, stderr) { var currentCheckItem = _parsedStateChecks[_currentStateCheckIndex] var currentCommand = currentCheckItem.command if (exitCode === 0) { // Command succeeded, this is the active state _isHot = true _activeStateIcon = currentCheckItem.icon || widgetSettings.icon || "heart" } else { // Command failed, try next one _currentStateCheckIndex++ _checkNextState() } } } Timer { id: stateUpdateTimer interval: 200 running: false repeat: false onTriggered: { if (enableOnStateLogic && _parsedStateChecks.length > 0) { _currentStateCheckIndex = 0 _checkNextState() } else { _isHot = false _activeStateIcon = widgetSettings.icon || "heart" } } } function _checkNextState() { if (_currentStateCheckIndex < _parsedStateChecks.length) { var currentCheckItem = _parsedStateChecks[_currentStateCheckIndex] if (currentCheckItem && currentCheckItem.command) { stateCheckProcessExecutor.running = true } else { _currentStateCheckIndex++ _checkNextState() } } else { // All checks failed _isHot = false _activeStateIcon = widgetSettings.icon || "heart" } } function updateState() { if (!enableOnStateLogic || _parsedStateChecks.length === 0) { _isHot = false; _activeStateIcon = widgetSettings.icon || "heart"; return; } stateUpdateTimer.restart(); } function _buildTooltipText() { let tooltip = generalTooltipText if (onClickedCommand) { tooltip += `\nLeft click: ${onClickedCommand}` } if (onRightClickedCommand) { tooltip += `\nRight click: ${onRightClickedCommand}` } if (onMiddleClickedCommand) { tooltip += `\nMiddle click: ${onMiddleClickedCommand}` } return tooltip } implicitWidth: button.implicitWidth implicitHeight: button.implicitHeight NIconButtonHot { id: button icon: _activeStateIcon hot: _isHot tooltipText: _buildTooltipText() onClicked: { if (onClickedCommand) { Quickshell.execDetached(["sh", "-c", onClickedCommand]) updateState() } } onRightClicked: { if (onRightClickedCommand) { Quickshell.execDetached(["sh", "-c", onRightClickedCommand]) updateState() } } onMiddleClicked: { if (onMiddleClickedCommand) { Quickshell.execDetached(["sh", "-c", onMiddleClickedCommand]) updateState() } } } }