mirror of
https://github.com/zoriya/noctalia-shell.git
synced 2026-06-08 12:52:24 +00:00
feat: Custom buttons now support wheel actions
This commit is contained in:
@@ -145,6 +145,23 @@
|
|||||||
"label": "Middle click",
|
"label": "Middle click",
|
||||||
"update-text": "Update displayed text on middle-click"
|
"update-text": "Update displayed text on middle-click"
|
||||||
},
|
},
|
||||||
|
"wheel": {
|
||||||
|
"description": "Command to execute when the scroll wheel is used.\nUse $delta for the scroll wheel delta in the command",
|
||||||
|
"label": "Scroll wheel",
|
||||||
|
"update-text": "Update displayed text on scroll"
|
||||||
|
},
|
||||||
|
"wheel-mode-separate": {
|
||||||
|
"label": "Separate wheel commands",
|
||||||
|
"description": "Enable separate commands for wheel up and down"
|
||||||
|
},
|
||||||
|
"wheel-up": {
|
||||||
|
"description": "Command to execute when the scroll wheel is scrolled up.",
|
||||||
|
"label": "Wheel up command"
|
||||||
|
},
|
||||||
|
"wheel-down": {
|
||||||
|
"description": "Command to execute when the scroll wheel is scrolled down.",
|
||||||
|
"label": "Wheel down command"
|
||||||
|
},
|
||||||
"parse-json": {
|
"parse-json": {
|
||||||
"description": "Parse the command output as a JSON object to dynamically set text and icon.",
|
"description": "Parse the command output as a JSON object to dynamically set text and icon.",
|
||||||
"label": "Parse output as JSON"
|
"label": "Parse output as JSON"
|
||||||
|
|||||||
@@ -39,12 +39,21 @@ Item {
|
|||||||
readonly property bool rightClickUpdateText: widgetSettings.rightClickUpdateText ?? widgetMetadata.rightClickUpdateText
|
readonly property bool rightClickUpdateText: widgetSettings.rightClickUpdateText ?? widgetMetadata.rightClickUpdateText
|
||||||
readonly property string middleClickExec: widgetSettings.middleClickExec || widgetMetadata.middleClickExec
|
readonly property string middleClickExec: widgetSettings.middleClickExec || widgetMetadata.middleClickExec
|
||||||
readonly property bool middleClickUpdateText: widgetSettings.middleClickUpdateText ?? widgetMetadata.middleClickUpdateText
|
readonly property bool middleClickUpdateText: widgetSettings.middleClickUpdateText ?? widgetMetadata.middleClickUpdateText
|
||||||
|
readonly property string wheelExec: widgetSettings.wheelExec || widgetMetadata.wheelExec
|
||||||
|
readonly property string wheelUpExec: widgetSettings.wheelUpExec || widgetMetadata.wheelUpExec
|
||||||
|
readonly property string wheelDownExec: widgetSettings.wheelDownExec || widgetMetadata.wheelDownExec
|
||||||
|
readonly property string wheelMode: widgetSettings.wheelMode || widgetMetadata.wheelMode
|
||||||
|
readonly property bool wheelUpdateText: widgetSettings.wheelUpdateText ?? widgetMetadata.wheelUpdateText
|
||||||
|
readonly property bool wheelUpUpdateText: widgetSettings.wheelUpUpdateText ?? widgetMetadata.wheelUpUpdateText
|
||||||
|
readonly property bool wheelDownUpdateText: widgetSettings.wheelDownUpdateText ?? widgetMetadata.wheelDownUpdateText
|
||||||
readonly property string textCommand: widgetSettings.textCommand !== undefined ? widgetSettings.textCommand : (widgetMetadata.textCommand || "")
|
readonly property string textCommand: widgetSettings.textCommand !== undefined ? widgetSettings.textCommand : (widgetMetadata.textCommand || "")
|
||||||
readonly property bool textStream: widgetSettings.textStream !== undefined ? widgetSettings.textStream : (widgetMetadata.textStream || false)
|
readonly property bool textStream: widgetSettings.textStream !== undefined ? widgetSettings.textStream : (widgetMetadata.textStream || false)
|
||||||
readonly property int textIntervalMs: widgetSettings.textIntervalMs !== undefined ? widgetSettings.textIntervalMs : (widgetMetadata.textIntervalMs || 3000)
|
readonly property int textIntervalMs: widgetSettings.textIntervalMs !== undefined ? widgetSettings.textIntervalMs : (widgetMetadata.textIntervalMs || 3000)
|
||||||
readonly property string textCollapse: widgetSettings.textCollapse !== undefined ? widgetSettings.textCollapse : (widgetMetadata.textCollapse || "")
|
readonly property string textCollapse: widgetSettings.textCollapse !== undefined ? widgetSettings.textCollapse : (widgetMetadata.textCollapse || "")
|
||||||
readonly property bool parseJson: widgetSettings.parseJson !== undefined ? widgetSettings.parseJson : (widgetMetadata.parseJson || false)
|
readonly property bool parseJson: widgetSettings.parseJson !== undefined ? widgetSettings.parseJson : (widgetMetadata.parseJson || false)
|
||||||
readonly property bool hasExec: (leftClickExec || rightClickExec || middleClickExec)
|
readonly property bool hasExec: (leftClickExec || rightClickExec || middleClickExec ||
|
||||||
|
(wheelMode === "unified" && wheelExec) ||
|
||||||
|
(wheelMode === "separate" && (wheelUpExec || wheelDownExec)))
|
||||||
|
|
||||||
implicitWidth: pill.width
|
implicitWidth: pill.width
|
||||||
implicitHeight: pill.height
|
implicitHeight: pill.height
|
||||||
@@ -73,6 +82,16 @@ Item {
|
|||||||
if (middleClickExec !== "") {
|
if (middleClickExec !== "") {
|
||||||
tooltipLines.push(`Middle click: ${middleClickExec}.`);
|
tooltipLines.push(`Middle click: ${middleClickExec}.`);
|
||||||
}
|
}
|
||||||
|
if (wheelMode === "unified" && wheelExec !== "") {
|
||||||
|
tooltipLines.push(`Wheel: ${wheelExec}.`);
|
||||||
|
} else if (wheelMode === "separate") {
|
||||||
|
if (wheelUpExec !== "") {
|
||||||
|
tooltipLines.push(`Wheel up: ${wheelUpExec}.`);
|
||||||
|
}
|
||||||
|
if (wheelDownExec !== "") {
|
||||||
|
tooltipLines.push(`Wheel down: ${wheelDownExec}.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_dynamicTooltip !== "") {
|
if (_dynamicTooltip !== "") {
|
||||||
@@ -92,6 +111,7 @@ Item {
|
|||||||
onClicked: root.onClicked()
|
onClicked: root.onClicked()
|
||||||
onRightClicked: root.onRightClicked()
|
onRightClicked: root.onRightClicked()
|
||||||
onMiddleClicked: root.onMiddleClicked()
|
onMiddleClicked: root.onMiddleClicked()
|
||||||
|
onWheel: delta => root.onWheel(delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal state for dynamic text
|
// Internal state for dynamic text
|
||||||
@@ -382,4 +402,81 @@ Item {
|
|||||||
textProc.command = ["sh", "-lc", textCommand];
|
textProc.command = ["sh", "-lc", textCommand];
|
||||||
textProc.running = true;
|
textProc.running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onWheel(delta) {
|
||||||
|
if (wheelMode === "unified" && wheelExec) {
|
||||||
|
let normalizedDelta = delta > 0 ? 1 : -1;
|
||||||
|
|
||||||
|
let command = wheelExec.replace(/\$delta([+\-*/]\d+)?/g, function(match, operation) {
|
||||||
|
if (operation) {
|
||||||
|
try {
|
||||||
|
let operator = operation.charAt(0);
|
||||||
|
let operand = parseInt(operation.substring(1));
|
||||||
|
|
||||||
|
let 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) {
|
||||||
|
Logger.w("CustomButton", `Error evaluating expression: ${match}, using normalized value ${normalizedDelta}`);
|
||||||
|
return normalizedDelta.toString();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return normalizedDelta.toString();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Quickshell.execDetached(["sh", "-c", command])
|
||||||
|
Logger.i("CustomButton", `Executing command: ${command}`)
|
||||||
|
} else if (wheelMode === "separate") {
|
||||||
|
if ((delta > 0 && wheelUpExec) || (delta < 0 && wheelDownExec)) {
|
||||||
|
let commandExec = delta > 0 ? wheelUpExec : wheelDownExec;
|
||||||
|
let normalizedDelta = delta > 0 ? 1 : -1;
|
||||||
|
|
||||||
|
let command = commandExec.replace(/\$delta([+\-*/]\d+)?/g, function(match, operation) {
|
||||||
|
if (operation) {
|
||||||
|
try {
|
||||||
|
let operator = operation.charAt(0);
|
||||||
|
let operand = parseInt(operation.substring(1));
|
||||||
|
|
||||||
|
let 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) {
|
||||||
|
Logger.w("CustomButton", `Error evaluating expression: ${match}, using normalized value ${normalizedDelta}`);
|
||||||
|
return normalizedDelta.toString();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return normalizedDelta.toString();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Quickshell.execDetached(["sh", "-c", command])
|
||||||
|
Logger.i("CustomButton", `Executing command: ${command}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!textStream) {
|
||||||
|
if (wheelMode === "unified" && wheelUpdateText) {
|
||||||
|
runTextCommand()
|
||||||
|
} else if (wheelMode === "separate") {
|
||||||
|
if ((delta > 0 && wheelUpUpdateText) || (delta < 0 && wheelDownUpdateText)) {
|
||||||
|
runTextCommand()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,13 @@ ColumnLayout {
|
|||||||
settings.rightClickUpdateText = rightClickUpdateText.checked;
|
settings.rightClickUpdateText = rightClickUpdateText.checked;
|
||||||
settings.middleClickExec = middleClickExecInput.text;
|
settings.middleClickExec = middleClickExecInput.text;
|
||||||
settings.middleClickUpdateText = middleClickUpdateText.checked;
|
settings.middleClickUpdateText = middleClickUpdateText.checked;
|
||||||
|
settings.wheelMode = separateWheelToggle.internalChecked ? "separate" : "unified";
|
||||||
|
settings.wheelExec = wheelExecInput.text;
|
||||||
|
settings.wheelUpExec = wheelUpExecInput.text;
|
||||||
|
settings.wheelDownExec = wheelDownExecInput.text;
|
||||||
|
settings.wheelUpdateText = wheelUpdateText.checked;
|
||||||
|
settings.wheelUpUpdateText = wheelUpUpdateText.checked;
|
||||||
|
settings.wheelDownUpdateText = wheelDownUpdateText.checked;
|
||||||
settings.textCommand = textCommandInput.text;
|
settings.textCommand = textCommandInput.text;
|
||||||
settings.textCollapse = textCollapseInput.text;
|
settings.textCollapse = textCollapseInput.text;
|
||||||
settings.textStream = valueTextStream;
|
settings.textStream = valueTextStream;
|
||||||
@@ -141,6 +148,103 @@ ColumnLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wheel command settings
|
||||||
|
NToggle {
|
||||||
|
id: separateWheelToggle
|
||||||
|
Layout.fillWidth: true
|
||||||
|
label: I18n.tr("bar.widget-settings.custom-button.wheel-mode-separate.label", "Separate wheel commands")
|
||||||
|
description: I18n.tr("bar.widget-settings.custom-button.wheel-mode-separate.description", "Enable separate commands for wheel up and down")
|
||||||
|
property bool internalChecked: (widgetData?.wheelMode || widgetMetadata?.wheelMode || "unified") === "separate"
|
||||||
|
checked: internalChecked
|
||||||
|
onToggled: checked => {
|
||||||
|
internalChecked = checked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: unifiedWheelLayout
|
||||||
|
visible: !separateWheelToggle.checked
|
||||||
|
spacing: Style.marginM
|
||||||
|
|
||||||
|
NTextInput {
|
||||||
|
id: wheelExecInput
|
||||||
|
Layout.fillWidth: true
|
||||||
|
label: I18n.tr("bar.widget-settings.custom-button.wheel.label")
|
||||||
|
description: I18n.tr("bar.widget-settings.custom-button.wheel.description")
|
||||||
|
placeholderText: I18n.tr("placeholders.enter-command")
|
||||||
|
text: widgetData?.wheelExec || widgetMetadata?.wheelExec || ""
|
||||||
|
}
|
||||||
|
|
||||||
|
NToggle {
|
||||||
|
id: wheelUpdateText
|
||||||
|
enabled: !valueTextStream
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
||||||
|
Layout.bottomMargin: Style.marginS
|
||||||
|
onEntered: TooltipService.show(wheelUpdateText, I18n.tr("bar.widget-settings.custom-button.wheel.update-text"), "auto")
|
||||||
|
onExited: TooltipService.hide()
|
||||||
|
checked: widgetData?.wheelUpdateText ?? widgetMetadata?.wheelUpdateText
|
||||||
|
onToggled: isChecked => checked = isChecked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: separateWheelLayout
|
||||||
|
visible: separateWheelToggle.checked
|
||||||
|
spacing: Style.marginS
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: Style.marginM
|
||||||
|
|
||||||
|
NTextInput {
|
||||||
|
id: wheelUpExecInput
|
||||||
|
Layout.fillWidth: true
|
||||||
|
label: I18n.tr("bar.widget-settings.custom-button.wheel-up.label")
|
||||||
|
description: I18n.tr("bar.widget-settings.custom-button.wheel-up.description")
|
||||||
|
placeholderText: I18n.tr("placeholders.enter-command")
|
||||||
|
text: widgetData?.wheelUpExec || widgetMetadata?.wheelUpExec || ""
|
||||||
|
}
|
||||||
|
|
||||||
|
NToggle {
|
||||||
|
id: wheelUpUpdateText
|
||||||
|
enabled: !valueTextStream
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
||||||
|
Layout.bottomMargin: Style.marginS
|
||||||
|
onEntered: TooltipService.show(wheelUpUpdateText, I18n.tr("bar.widget-settings.custom-button.wheel.update-text"), "auto")
|
||||||
|
onExited: TooltipService.hide()
|
||||||
|
checked: (widgetData?.wheelUpUpdateText !== undefined) ? widgetData.wheelUpUpdateText : (widgetMetadata?.wheelUpUpdateText ?? false)
|
||||||
|
onToggled: isChecked => checked = isChecked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: Style.marginM
|
||||||
|
|
||||||
|
NTextInput {
|
||||||
|
id: wheelDownExecInput
|
||||||
|
Layout.fillWidth: true
|
||||||
|
label: I18n.tr("bar.widget-settings.custom-button.wheel-down.label")
|
||||||
|
description: I18n.tr("bar.widget-settings.custom-button.wheel-down.description")
|
||||||
|
placeholderText: I18n.tr("placeholders.enter-command")
|
||||||
|
text: widgetData?.wheelDownExec || widgetMetadata?.wheelDownExec || ""
|
||||||
|
}
|
||||||
|
|
||||||
|
NToggle {
|
||||||
|
id: wheelDownUpdateText
|
||||||
|
enabled: !valueTextStream
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
|
||||||
|
Layout.bottomMargin: Style.marginS
|
||||||
|
onEntered: TooltipService.show(wheelDownUpdateText, I18n.tr("bar.widget-settings.custom-button.wheel.update-text"), "auto")
|
||||||
|
onExited: TooltipService.hide()
|
||||||
|
checked: (widgetData?.wheelDownUpdateText !== undefined) ? widgetData.wheelDownUpdateText : (widgetMetadata?.wheelDownUpdateText ?? false)
|
||||||
|
onToggled: isChecked => checked = isChecked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NDivider {
|
NDivider {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,6 +124,13 @@ Singleton {
|
|||||||
"textIntervalMs": 3000,
|
"textIntervalMs": 3000,
|
||||||
"textCollapse": "",
|
"textCollapse": "",
|
||||||
"parseJson": false,
|
"parseJson": false,
|
||||||
|
"wheelExec": "",
|
||||||
|
"wheelUpExec": "",
|
||||||
|
"wheelDownExec": "",
|
||||||
|
"wheelMode": "unified",
|
||||||
|
"wheelUpdateText": false,
|
||||||
|
"wheelUpUpdateText": false,
|
||||||
|
"wheelDownUpdateText": false,
|
||||||
"maxTextLength": {
|
"maxTextLength": {
|
||||||
"horizontal": 10,
|
"horizontal": 10,
|
||||||
"vertical": 10
|
"vertical": 10
|
||||||
|
|||||||
Reference in New Issue
Block a user