mirror of
https://github.com/zoriya/noctalia-shell.git
synced 2026-06-07 04:15:26 +00:00
add support for streaming command outputs in CustomButton
This commit is contained in:
@@ -1073,9 +1073,14 @@
|
|||||||
"description": "Command to execute when the button is middle-clicked."
|
"description": "Command to execute when the button is middle-clicked."
|
||||||
},
|
},
|
||||||
"dynamic-text": "Dynamic text",
|
"dynamic-text": "Dynamic text",
|
||||||
|
"text-stream": {
|
||||||
|
"label": "Stream",
|
||||||
|
"description": "Streamed lines from the command will be displayed as text on the button."
|
||||||
|
},
|
||||||
"display-command-output": {
|
"display-command-output": {
|
||||||
"label": "Display Command Output",
|
"label": "Display Command Output",
|
||||||
"description": "Enter a command to run at a regular interval. The first line of its output will be displayed as text."
|
"description": "Enter a command to run at a regular interval. The first line of its output will be displayed as text.",
|
||||||
|
"stream-description": "Enter a command to run continuously."
|
||||||
},
|
},
|
||||||
"refresh-interval": {
|
"refresh-interval": {
|
||||||
"label": "Refresh interval",
|
"label": "Refresh interval",
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ Item {
|
|||||||
readonly property string rightClickExec: widgetSettings.rightClickExec || widgetMetadata.rightClickExec
|
readonly property string rightClickExec: widgetSettings.rightClickExec || widgetMetadata.rightClickExec
|
||||||
readonly property string middleClickExec: widgetSettings.middleClickExec || widgetMetadata.middleClickExec
|
readonly property string middleClickExec: widgetSettings.middleClickExec || widgetMetadata.middleClickExec
|
||||||
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 int textIntervalMs: widgetSettings.textIntervalMs !== undefined ? widgetSettings.textIntervalMs : (widgetMetadata.textIntervalMs || 3000)
|
readonly property int textIntervalMs: widgetSettings.textIntervalMs !== undefined ? widgetSettings.textIntervalMs : (widgetMetadata.textIntervalMs || 3000)
|
||||||
readonly property bool hasExec: (leftClickExec || rightClickExec || middleClickExec)
|
readonly property bool hasExec: (leftClickExec || rightClickExec || middleClickExec)
|
||||||
|
|
||||||
@@ -84,29 +85,45 @@ Item {
|
|||||||
id: refreshTimer
|
id: refreshTimer
|
||||||
interval: Math.max(250, textIntervalMs)
|
interval: Math.max(250, textIntervalMs)
|
||||||
repeat: true
|
repeat: true
|
||||||
running: (textCommand && textCommand.length > 0)
|
running: !textStream && textCommand && textCommand.length > 0
|
||||||
triggeredOnStart: true
|
triggeredOnStart: true
|
||||||
onTriggered: {
|
onTriggered: root.runTextCommand()
|
||||||
if (!textCommand || textCommand.length === 0)
|
}
|
||||||
return
|
|
||||||
if (textProc.running)
|
// Restart exited text stream commands after a delay
|
||||||
return
|
Timer {
|
||||||
textProc.command = ["sh", "-lc", textCommand]
|
id: restartTimer
|
||||||
textProc.running = true
|
interval: 1000
|
||||||
|
running: textStream && !textProc.running
|
||||||
|
onTriggered: root.runTextCommand()
|
||||||
|
}
|
||||||
|
|
||||||
|
SplitParser {
|
||||||
|
id: textStdoutSplit
|
||||||
|
onRead: (line) => _dynamicText = String(line || "").trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
StdioCollector {
|
||||||
|
id: textStdoutCollect
|
||||||
|
onStreamFinished: () => {
|
||||||
|
var out = String(this.text || "").trim()
|
||||||
|
if (out.indexOf("\n") !== -1) {
|
||||||
|
out = out.split("\n")[0]
|
||||||
|
}
|
||||||
|
_dynamicText = out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: textProc
|
id: textProc
|
||||||
stdout: StdioCollector {}
|
stdout: textStream ? textStdoutSplit : textStdoutCollect
|
||||||
stderr: StdioCollector {}
|
stderr: StdioCollector {}
|
||||||
onExited: (exitCode, exitStatus) => {
|
onExited: (exitCode, exitStatus) => {
|
||||||
var out = String(stdout.text || "").trim()
|
if (textStream) {
|
||||||
if (out.indexOf("\n") !== -1) {
|
Logger.w("CustomButton", `Streaming text command exited (code: ${exitCode}), restarting...`)
|
||||||
out = out.split("\n")[0]
|
return
|
||||||
}
|
}
|
||||||
_dynamicText = out
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClicked() {
|
function onClicked() {
|
||||||
@@ -134,4 +151,13 @@ Item {
|
|||||||
Logger.i("CustomButton", `Executing command: ${middleClickExec}`)
|
Logger.i("CustomButton", `Executing command: ${middleClickExec}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function runTextCommand() {
|
||||||
|
if (!textCommand || textCommand.length === 0)
|
||||||
|
return
|
||||||
|
if (textProc.running)
|
||||||
|
return
|
||||||
|
textProc.command = ["sh", "-lc", textCommand]
|
||||||
|
textProc.running = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ ColumnLayout {
|
|||||||
property var widgetMetadata: null
|
property var widgetMetadata: null
|
||||||
|
|
||||||
property string valueIcon: widgetData.icon !== undefined ? widgetData.icon : widgetMetadata.icon
|
property string valueIcon: widgetData.icon !== undefined ? widgetData.icon : widgetMetadata.icon
|
||||||
|
property bool valueTextStream: widgetData.textStream !== undefined ? widgetData.textStream : widgetMetadata.textStream
|
||||||
|
|
||||||
function saveSettings() {
|
function saveSettings() {
|
||||||
var settings = Object.assign({}, widgetData || {})
|
var settings = Object.assign({}, widgetData || {})
|
||||||
@@ -22,6 +23,7 @@ ColumnLayout {
|
|||||||
settings.rightClickExec = rightClickExecInput.text
|
settings.rightClickExec = rightClickExecInput.text
|
||||||
settings.middleClickExec = middleClickExecInput.text
|
settings.middleClickExec = middleClickExecInput.text
|
||||||
settings.textCommand = textCommandInput.text
|
settings.textCommand = textCommandInput.text
|
||||||
|
settings.textStream = valueTextStream
|
||||||
settings.textIntervalMs = parseInt(textIntervalInput.text || textIntervalInput.placeholderText, 10)
|
settings.textIntervalMs = parseInt(textIntervalInput.text || textIntervalInput.placeholderText, 10)
|
||||||
return settings
|
return settings
|
||||||
}
|
}
|
||||||
@@ -90,11 +92,21 @@ ColumnLayout {
|
|||||||
label: I18n.tr("bar.widget-settings.custom-button.dynamic-text")
|
label: I18n.tr("bar.widget-settings.custom-button.dynamic-text")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NToggle {
|
||||||
|
id: textStreamInput
|
||||||
|
label: I18n.tr("bar.widget-settings.custom-button.text-stream.label")
|
||||||
|
description: I18n.tr("bar.widget-settings.custom-button.text-stream.description")
|
||||||
|
checked: valueTextStream
|
||||||
|
onToggled: checked => valueTextStream = checked
|
||||||
|
}
|
||||||
|
|
||||||
NTextInput {
|
NTextInput {
|
||||||
id: textCommandInput
|
id: textCommandInput
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
label: I18n.tr("bar.widget-settings.custom-button.display-command-output.label")
|
label: I18n.tr("bar.widget-settings.custom-button.display-command-output.label")
|
||||||
description: I18n.tr("bar.widget-settings.custom-button.display-command-output.description")
|
description: valueTextStream ?
|
||||||
|
I18n.tr("bar.widget-settings.custom-button.display-command-output.stream-description") :
|
||||||
|
I18n.tr("bar.widget-settings.custom-button.display-command-output.description")
|
||||||
placeholderText: I18n.tr("placeholders.command-example")
|
placeholderText: I18n.tr("placeholders.command-example")
|
||||||
text: widgetData?.textCommand || widgetMetadata.textCommand
|
text: widgetData?.textCommand || widgetMetadata.textCommand
|
||||||
}
|
}
|
||||||
@@ -102,6 +114,7 @@ ColumnLayout {
|
|||||||
NTextInput {
|
NTextInput {
|
||||||
id: textIntervalInput
|
id: textIntervalInput
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
visible: !valueTextStream
|
||||||
label: I18n.tr("bar.widget-settings.custom-button.refresh-interval.label")
|
label: I18n.tr("bar.widget-settings.custom-button.refresh-interval.label")
|
||||||
description: I18n.tr("bar.widget-settings.custom-button.refresh-interval.description")
|
description: I18n.tr("bar.widget-settings.custom-button.refresh-interval.description")
|
||||||
placeholderText: String(widgetMetadata.textIntervalMs || 3000)
|
placeholderText: String(widgetMetadata.textIntervalMs || 3000)
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ Singleton {
|
|||||||
"rightClickExec": "",
|
"rightClickExec": "",
|
||||||
"middleClickExec": "",
|
"middleClickExec": "",
|
||||||
"textCommand": "",
|
"textCommand": "",
|
||||||
|
"textStream": false,
|
||||||
"textIntervalMs": 3000
|
"textIntervalMs": 3000
|
||||||
},
|
},
|
||||||
"KeyboardLayout": {
|
"KeyboardLayout": {
|
||||||
|
|||||||
Reference in New Issue
Block a user