From ec92295a9818d119c49d4d1880e674b114109276 Mon Sep 17 00:00:00 2001
From: loner <2788892716@qq.com>
Date: Thu, 20 Nov 2025 16:03:32 +0800
Subject: [PATCH 1/9] Enhance custom tooltip parsing
---
Modules/Bar/Widgets/CustomButton.qml | 24 ++++++++++++++++--------
1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/Modules/Bar/Widgets/CustomButton.qml b/Modules/Bar/Widgets/CustomButton.qml
index a2ef18f7..62867475 100644
--- a/Modules/Bar/Widgets/CustomButton.qml
+++ b/Modules/Bar/Widgets/CustomButton.qml
@@ -247,17 +247,25 @@ Item {
}
function toHtml(str) {
- const htmlRegex = /<\/?[a-zA-Z][\s\S]*>/;
+ const htmlTagRegex = /<\/?[a-zA-Z][^>]*>/g;
+ const placeholders = [];
+ let i = 0;
+ const protectedStr = str.replace(htmlTagRegex, tag => {
+ placeholders.push(tag);
+ return `___HTML_TAG_${i++}___`;
+ });
- if (htmlRegex.test(str)) {
- return str;
- }
+ let escaped = protectedStr
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'")
+ .replace(/\r\n|\r|\n/g, "
");
- const escaped = str.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'");
+ escaped = escaped.replace(/___HTML_TAG_(\d+)___/g, (_, index) => placeholders[Number(index)]);
- const withBreaks = escaped.replace(/\r\n|\r|\n/g, "
");
-
- return withBreaks;
+ return escaped;
}
function runTextCommand() {
From 6a840769ed4b3ad60d76a5ed18603fce8c130621 Mon Sep 17 00:00:00 2001
From: loner <2788892716@qq.com>
Date: Thu, 20 Nov 2025 17:13:16 +0800
Subject: [PATCH 2/9] Fix(BarPillVertical): Improve vertical text positioning
and spacing
---
Modules/Bar/Extras/BarPillVertical.qml | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/Modules/Bar/Extras/BarPillVertical.qml b/Modules/Bar/Extras/BarPillVertical.qml
index 6ac17686..ff0898c3 100644
--- a/Modules/Bar/Extras/BarPillVertical.qml
+++ b/Modules/Bar/Extras/BarPillVertical.qml
@@ -49,10 +49,9 @@ Item {
// Sizing logic for vertical bars
readonly property int buttonSize: Style.capsuleHeight
readonly property int pillHeight: buttonSize
- readonly property int pillPaddingVertical: 3 * 2 // Very precise adjustment don't replace by Style.margin
readonly property int pillOverlap: Math.round(buttonSize * 0.5)
- readonly property int maxPillWidth: rotateText ? Math.max(buttonSize, Math.round(textItem.implicitHeight + pillPaddingVertical * 2)) : buttonSize
- readonly property int maxPillHeight: rotateText ? Math.max(1, Math.round(textItem.implicitWidth + pillPaddingVertical * 2 + Math.round(iconCircle.height / 4))) : Math.max(1, Math.round(textItem.implicitHeight + pillPaddingVertical * 4))
+ readonly property int maxPillWidth: rotateText ? Math.max(buttonSize, Math.round(textItem.implicitHeight + Style.marginM * 2)) : buttonSize
+ readonly property int maxPillHeight: rotateText ? Math.max(1, Math.round(textItem.implicitWidth + Style.marginM * 2 + Math.round(iconCircle.height / 4))) : Math.max(1, Math.round(textItem.implicitHeight + Style.marginM * 2))
readonly property real iconSize: {
switch (root.density) {
@@ -130,7 +129,7 @@ Item {
id: textItem
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
- anchors.verticalCenterOffset: rotateText ? Math.round(iconCircle.height / 4) : getVerticalCenterOffset()
+ anchors.verticalCenterOffset: openDownward ? Style.marginXXS : -Style.marginXXS
rotation: rotateText ? -90 : 0
text: root.text + root.suffix
family: Settings.data.ui.fontFixed
@@ -143,11 +142,8 @@ Item {
visible: revealed
function getVerticalCenterOffset() {
- var offset = openDownward ? Math.round(pillPaddingVertical * 0.75) : -Math.round(pillPaddingVertical * 0.75);
- if (forceOpen) {
- offset += oppositeDirection ? -Style.marginXXS : Style.marginXXS;
- }
- return offset;
+ // A small, symmetrical offset to push the text slightly away from the icon's edge.
+ return openDownward ? Style.marginXS : -Style.marginXS;
}
}
Behavior on width {
From e549cfcb780c7b260fbec3f2e3885dbfe1951aa4 Mon Sep 17 00:00:00 2001
From: loner <2788892716@qq.com>
Date: Fri, 21 Nov 2025 05:04:14 +0800
Subject: [PATCH 3/9] feat: Use maxTextLength to Limit Custom Button Text
Length
---
Modules/Bar/Widgets/CustomButton.qml | 99 +++++++++++++++++++++++++++-
1 file changed, 97 insertions(+), 2 deletions(-)
diff --git a/Modules/Bar/Widgets/CustomButton.qml b/Modules/Bar/Widgets/CustomButton.qml
index 62867475..50b83ad8 100644
--- a/Modules/Bar/Widgets/CustomButton.qml
+++ b/Modules/Bar/Widgets/CustomButton.qml
@@ -102,6 +102,19 @@ Item {
property string _dynamicIcon: ""
property string _dynamicTooltip: ""
+ // Maximum length for text display before scrolling
+ readonly property int maxTextLength: 8
+ readonly property int _staticDuration: 6 // How many cycles to stay static at start/end
+
+ // Encapsulated state for scrolling text implementation
+ property var _scrollState: {
+ "originalText": "",
+ "needsScrolling": false,
+ "offset": 0,
+ "phase": 0, // 0=static start, 1=scrolling, 2=static end
+ "phaseCounter": 0
+ }
+
// Periodically run the text command (if set)
Timer {
id: refreshTimer
@@ -120,6 +133,54 @@ Item {
onTriggered: root.runTextCommand()
}
+ // Timer for scrolling text display
+ Timer {
+ id: scrollTimer
+ interval: 300
+ repeat: true
+ running: false
+ onTriggered: {
+ if (_scrollState.needsScrolling && _scrollState.originalText.length > maxTextLength) {
+ // Traditional marquee with pause at beginning and end
+ if (_scrollState.phase === 0) { // Static at beginning
+ _dynamicText = _scrollState.originalText.substring(0, Math.min(maxTextLength, _scrollState.originalText.length));
+ _scrollState.phaseCounter++;
+ if (_scrollState.phaseCounter >= _staticDuration) {
+ _scrollState.phaseCounter = 0;
+ _scrollState.phase = 1; // Move to scrolling
+ }
+ } else if (_scrollState.phase === 1) { // Scrolling
+ _scrollState.offset++;
+ var start = _scrollState.offset;
+ var end = start + maxTextLength;
+
+ if (start >= _scrollState.originalText.length - maxTextLength) {
+ // Reached or passed the end, ensure we show the last part
+ var textEnd = _scrollState.originalText.length;
+ var textStart = Math.max(0, textEnd - maxTextLength);
+ _dynamicText = _scrollState.originalText.substring(textStart, textEnd);
+ _scrollState.phase = 2; // Move to static end phase
+ _scrollState.phaseCounter = 0;
+ } else {
+ _dynamicText = _scrollState.originalText.substring(start, end);
+ }
+ } else if (_scrollState.phase === 2) { // Static at end
+ // Ensure end text is displayed correctly
+ var textEnd = _scrollState.originalText.length;
+ var textStart = Math.max(0, textEnd - maxTextLength);
+ _dynamicText = _scrollState.originalText.substring(textStart, textEnd);
+ _scrollState.phaseCounter++;
+ if (_scrollState.phaseCounter >= _staticDuration) {
+ // Do NOT loop back to start, just stop scrolling
+ scrollTimer.stop();
+ }
+ }
+ } else {
+ scrollTimer.stop();
+ }
+ }
+ }
+
SplitParser {
id: textStdoutSplit
onRead: line => root.parseDynamicContent(line)
@@ -162,16 +223,33 @@ Item {
let tooltip = parsed.tooltip || "";
if (checkCollapse(text)) {
+ _scrollState.originalText = "";
_dynamicText = "";
_dynamicIcon = "";
_dynamicTooltip = "";
+ _scrollState.needsScrolling = false;
+ _scrollState.phase = 0;
+ _scrollState.phaseCounter = 0;
return;
}
- _dynamicText = text;
+ _scrollState.originalText = text;
+ _scrollState.needsScrolling = text.length > maxTextLength;
+ if (_scrollState.needsScrolling) {
+ // Start with the beginning of the text
+ _dynamicText = text.substring(0, maxTextLength);
+ _scrollState.phase = 0; // Start at phase 0 (static beginning)
+ _scrollState.phaseCounter = 0;
+ _scrollState.offset = 0;
+ scrollTimer.start(); // Start the scrolling timer
+ } else {
+ _dynamicText = text;
+ scrollTimer.stop();
+ }
_dynamicIcon = icon;
_dynamicTooltip = toHtml(tooltip);
+ _scrollState.offset = 0;
return;
} catch (e) {
Logger.w("CustomButton", `Failed to parse JSON. Content: "${lineToParse}"`);
@@ -179,15 +257,32 @@ Item {
}
if (checkCollapse(contentStr)) {
+ _scrollState.originalText = "";
_dynamicText = "";
_dynamicIcon = "";
_dynamicTooltip = "";
+ _scrollState.needsScrolling = false;
+ _scrollState.phase = 0;
+ _scrollState.phaseCounter = 0;
return;
}
- _dynamicText = contentStr;
+ _scrollState.originalText = contentStr;
+ _scrollState.needsScrolling = contentStr.length > maxTextLength;
+ if (_scrollState.needsScrolling) {
+ // Start with the beginning of the text
+ _dynamicText = contentStr.substring(0, maxTextLength);
+ _scrollState.phase = 0; // Start at phase 0 (static beginning)
+ _scrollState.phaseCounter = 0;
+ _scrollState.offset = 0;
+ scrollTimer.start(); // Start the scrolling timer
+ } else {
+ _dynamicText = contentStr;
+ scrollTimer.stop();
+ }
_dynamicIcon = "";
_dynamicTooltip = toHtml(contentStr);
+ _scrollState.offset = 0;
}
function checkCollapse(text) {
From 516fc47b68bbc47adbba26a1d2133cc928467bf0 Mon Sep 17 00:00:00 2001
From: loner <2788892716@qq.com>
Date: Fri, 21 Nov 2025 08:19:29 +0800
Subject: [PATCH 4/9] feat: Replace hideTextInVerticalBar with maxTextLength
object
- Replace boolean hideTextInVerticalBar with maxTextLength object that has
separate horizontal and vertical properties for more flexible text length control
- Add NSpinBox controls in settings UI to configure both horizontal and
vertical max text length independently
- Update CustomButton widget to use new maxTextLength structure and
implement text scrolling based on direction-specific limits
- Set default values to 10 for both horizontal and vertical (was 20/0)
- Update translations and widget registry metadata accordingly
- When vertical maxTextLength is 0, text is completely hidden (preserving
original hideTextInVerticalBar: true behavior)
This allows users to set different text length limits for horizontal and
vertical bar orientations, providing more granular control over text display.
---
Assets/Translations/en.json | 10 ++--
Modules/Bar/Widgets/CustomButton.qml | 49 ++++++++++++-------
.../WidgetSettings/CustomButtonSettings.qml | 29 ++++++++---
Services/UI/BarWidgetRegistry.qml | 5 +-
4 files changed, 63 insertions(+), 30 deletions(-)
diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json
index 7ef497cf..ec385399 100644
--- a/Assets/Translations/en.json
+++ b/Assets/Translations/en.json
@@ -123,9 +123,13 @@
"stream-description": "Enter a command to run continuously."
},
"dynamic-text": "Dynamic text",
- "hide-vertical": {
- "description": "If enabled, the text from the command output will not be shown when the bar is in a vertical layout (left or right).",
- "label": "Hide text in vertical bar"
+ "max-text-length-horizontal": {
+ "description": "Maximum number of characters to show in horizontal bar (0 to hide text)",
+ "label": "Max text length (horizontal)"
+ },
+ "max-text-length-vertical": {
+ "description": "Maximum number of characters to show in vertical bar (0 to hide text)",
+ "label": "Max text length (vertical)"
},
"icon": {
"description": "Select an icon from the library.",
diff --git a/Modules/Bar/Widgets/CustomButton.qml b/Modules/Bar/Widgets/CustomButton.qml
index 50b83ad8..e73bad54 100644
--- a/Modules/Bar/Widgets/CustomButton.qml
+++ b/Modules/Bar/Widgets/CustomButton.qml
@@ -44,11 +44,8 @@ Item {
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 bool parseJson: widgetSettings.parseJson !== undefined ? widgetSettings.parseJson : (widgetMetadata.parseJson || false)
- readonly property bool hideTextInVerticalBar: widgetSettings.hideTextInVerticalBar !== undefined ? widgetSettings.hideTextInVerticalBar : (widgetMetadata.hideTextInVerticalBar || false)
readonly property bool hasExec: (leftClickExec || rightClickExec || middleClickExec)
- readonly property bool shouldShowText: !isVerticalBar || !hideTextInVerticalBar
-
implicitWidth: pill.width
implicitHeight: pill.height
@@ -58,9 +55,9 @@ Item {
screen: root.screen
oppositeDirection: BarService.getPillDirection(root)
icon: _dynamicIcon !== "" ? _dynamicIcon : customIcon
- text: shouldShowText ? _dynamicText : ""
+ text: (!isVerticalBar || currentMaxTextLength > 0) ? _dynamicText : ""
density: Settings.data.bar.density
- rotateText: isVerticalBar && !hideTextInVerticalBar
+ rotateText: isVerticalBar && currentMaxTextLength > 0
autoHide: false
forceOpen: _dynamicText !== ""
tooltipText: {
@@ -102,8 +99,19 @@ Item {
property string _dynamicIcon: ""
property string _dynamicTooltip: ""
- // Maximum length for text display before scrolling
- readonly property int maxTextLength: 8
+ // Maximum length for text display before scrolling (different values for horizontal and vertical)
+ readonly property var maxTextLength: {
+ "horizontal": ((widgetSettings && widgetSettings.maxTextLength && widgetSettings.maxTextLength.horizontal !== undefined) ?
+ widgetSettings.maxTextLength.horizontal :
+ ((widgetMetadata && widgetMetadata.maxTextLength && widgetMetadata.maxTextLength.horizontal !== undefined) ?
+ widgetMetadata.maxTextLength.horizontal :
+ 10)),
+ "vertical": ((widgetSettings && widgetSettings.maxTextLength && widgetSettings.maxTextLength.vertical !== undefined) ?
+ widgetSettings.maxTextLength.vertical :
+ ((widgetMetadata && widgetMetadata.maxTextLength && widgetMetadata.maxTextLength.vertical !== undefined) ?
+ widgetMetadata.maxTextLength.vertical :
+ 10))
+ }
readonly property int _staticDuration: 6 // How many cycles to stay static at start/end
// Encapsulated state for scrolling text implementation
@@ -115,12 +123,15 @@ Item {
"phaseCounter": 0
}
+ // Current max text length based on bar orientation
+ readonly property int currentMaxTextLength: isVerticalBar ? maxTextLength.vertical : maxTextLength.horizontal
+
// Periodically run the text command (if set)
Timer {
id: refreshTimer
interval: Math.max(250, textIntervalMs)
repeat: true
- running: shouldShowText && !textStream && textCommand && textCommand.length > 0
+ running: (!isVerticalBar || currentMaxTextLength > 0) && !textStream && textCommand && textCommand.length > 0
triggeredOnStart: true
onTriggered: root.runTextCommand()
}
@@ -129,7 +140,7 @@ Item {
Timer {
id: restartTimer
interval: 1000
- running: shouldShowText && textStream && !textProc.running
+ running: (!isVerticalBar || currentMaxTextLength > 0) && textStream && !textProc.running
onTriggered: root.runTextCommand()
}
@@ -140,10 +151,10 @@ Item {
repeat: true
running: false
onTriggered: {
- if (_scrollState.needsScrolling && _scrollState.originalText.length > maxTextLength) {
+ if (_scrollState.needsScrolling && _scrollState.originalText.length > currentMaxTextLength) {
// Traditional marquee with pause at beginning and end
if (_scrollState.phase === 0) { // Static at beginning
- _dynamicText = _scrollState.originalText.substring(0, Math.min(maxTextLength, _scrollState.originalText.length));
+ _dynamicText = _scrollState.originalText.substring(0, Math.min(currentMaxTextLength, _scrollState.originalText.length));
_scrollState.phaseCounter++;
if (_scrollState.phaseCounter >= _staticDuration) {
_scrollState.phaseCounter = 0;
@@ -152,12 +163,12 @@ Item {
} else if (_scrollState.phase === 1) { // Scrolling
_scrollState.offset++;
var start = _scrollState.offset;
- var end = start + maxTextLength;
+ var end = start + currentMaxTextLength;
- if (start >= _scrollState.originalText.length - maxTextLength) {
+ if (start >= _scrollState.originalText.length - currentMaxTextLength) {
// Reached or passed the end, ensure we show the last part
var textEnd = _scrollState.originalText.length;
- var textStart = Math.max(0, textEnd - maxTextLength);
+ var textStart = Math.max(0, textEnd - currentMaxTextLength);
_dynamicText = _scrollState.originalText.substring(textStart, textEnd);
_scrollState.phase = 2; // Move to static end phase
_scrollState.phaseCounter = 0;
@@ -167,7 +178,7 @@ Item {
} else if (_scrollState.phase === 2) { // Static at end
// Ensure end text is displayed correctly
var textEnd = _scrollState.originalText.length;
- var textStart = Math.max(0, textEnd - maxTextLength);
+ var textStart = Math.max(0, textEnd - currentMaxTextLength);
_dynamicText = _scrollState.originalText.substring(textStart, textEnd);
_scrollState.phaseCounter++;
if (_scrollState.phaseCounter >= _staticDuration) {
@@ -234,10 +245,10 @@ Item {
}
_scrollState.originalText = text;
- _scrollState.needsScrolling = text.length > maxTextLength;
+ _scrollState.needsScrolling = text.length > currentMaxTextLength && currentMaxTextLength > 0;
if (_scrollState.needsScrolling) {
// Start with the beginning of the text
- _dynamicText = text.substring(0, maxTextLength);
+ _dynamicText = text.substring(0, currentMaxTextLength);
_scrollState.phase = 0; // Start at phase 0 (static beginning)
_scrollState.phaseCounter = 0;
_scrollState.offset = 0;
@@ -268,10 +279,10 @@ Item {
}
_scrollState.originalText = contentStr;
- _scrollState.needsScrolling = contentStr.length > maxTextLength;
+ _scrollState.needsScrolling = contentStr.length > currentMaxTextLength && currentMaxTextLength > 0;
if (_scrollState.needsScrolling) {
// Start with the beginning of the text
- _dynamicText = contentStr.substring(0, maxTextLength);
+ _dynamicText = contentStr.substring(0, currentMaxTextLength);
_scrollState.phase = 0; // Start at phase 0 (static beginning)
_scrollState.phaseCounter = 0;
_scrollState.offset = 0;
diff --git a/Modules/Panels/Settings/Bar/WidgetSettings/CustomButtonSettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/CustomButtonSettings.qml
index dfbebd81..e209471b 100644
--- a/Modules/Panels/Settings/Bar/WidgetSettings/CustomButtonSettings.qml
+++ b/Modules/Panels/Settings/Bar/WidgetSettings/CustomButtonSettings.qml
@@ -16,7 +16,8 @@ ColumnLayout {
property string valueIcon: widgetData.icon !== undefined ? widgetData.icon : widgetMetadata.icon
property bool valueTextStream: widgetData.textStream !== undefined ? widgetData.textStream : widgetMetadata.textStream
property bool valueParseJson: widgetData.parseJson !== undefined ? widgetData.parseJson : widgetMetadata.parseJson
- property bool valueHideTextInVerticalBar: widgetData.hideTextInVerticalBar !== undefined ? widgetData.hideTextInVerticalBar : widgetMetadata.hideTextInVerticalBar
+ property int valueMaxTextLengthHorizontal: widgetData?.maxTextLength?.horizontal ?? widgetMetadata?.maxTextLength?.horizontal
+ property int valueMaxTextLengthVertical: widgetData?.maxTextLength?.vertical ?? widgetMetadata?.maxTextLength?.vertical
function saveSettings() {
var settings = Object.assign({}, widgetData || {});
@@ -31,7 +32,10 @@ ColumnLayout {
settings.textCollapse = textCollapseInput.text;
settings.textStream = valueTextStream;
settings.parseJson = valueParseJson;
- settings.hideTextInVerticalBar = valueHideTextInVerticalBar;
+ settings.maxTextLength = {
+ "horizontal": valueMaxTextLengthHorizontal,
+ "vertical": valueMaxTextLengthVertical
+ };
settings.textIntervalMs = parseInt(textIntervalInput.text || textIntervalInput.placeholderText, 10);
return settings;
}
@@ -145,11 +149,22 @@ ColumnLayout {
label: I18n.tr("bar.widget-settings.custom-button.dynamic-text")
}
- NToggle {
- label: I18n.tr("bar.widget-settings.custom-button.hide-vertical.label", "Hide text in vertical bar")
- description: I18n.tr("bar.widget-settings.custom-button.hide-vertical.description", "If enabled, the text from the command output will not be shown when the bar is in a vertical layout (left or right).")
- checked: valueHideTextInVerticalBar
- onToggled: checked => valueHideTextInVerticalBar = checked
+ NSpinBox {
+ label: I18n.tr("bar.widget-settings.custom-button.max-text-length-horizontal.label", "Max text length (horizontal)")
+ description: I18n.tr("bar.widget-settings.custom-button.max-text-length-horizontal.description", "Maximum number of characters to show in horizontal bar (0 to hide text)")
+ from: 0
+ to: 100
+ value: valueMaxTextLengthHorizontal
+ onValueChanged: valueMaxTextLengthHorizontal = value
+ }
+
+ NSpinBox {
+ label: I18n.tr("bar.widget-settings.custom-button.max-text-length-vertical.label", "Max text length (vertical)")
+ description: I18n.tr("bar.widget-settings.custom-button.max-text-length-vertical.description", "Maximum number of characters to show in vertical bar (0 to hide text)")
+ from: 0
+ to: 100
+ value: valueMaxTextLengthVertical
+ onValueChanged: valueMaxTextLengthVertical = value
}
NToggle {
diff --git a/Services/UI/BarWidgetRegistry.qml b/Services/UI/BarWidgetRegistry.qml
index 50923e27..f9423f68 100644
--- a/Services/UI/BarWidgetRegistry.qml
+++ b/Services/UI/BarWidgetRegistry.qml
@@ -124,7 +124,10 @@ Singleton {
"textIntervalMs": 3000,
"textCollapse": "",
"parseJson": false,
- "hideTextInVerticalBar": false
+ "maxTextLength": {
+ "horizontal": 10,
+ "vertical": 10
+ }
},
"KeyboardLayout": {
"allowUserSettings": true,
From 857d1dbbb6d7d283d1658bafe591477ab4ef7ba0 Mon Sep 17 00:00:00 2001
From: loner <2788892716@qq.com>
Date: Fri, 21 Nov 2025 09:04:43 +0800
Subject: [PATCH 5/9] feat: Update translation files for maxTextLength feature
---
Assets/Translations/de.json | 10 +++++++---
Assets/Translations/es.json | 10 +++++++---
Assets/Translations/fr.json | 10 +++++++---
Assets/Translations/nl.json | 10 +++++++---
Assets/Translations/pt.json | 10 +++++++---
Assets/Translations/ru.json | 10 +++++++---
Assets/Translations/tr.json | 10 +++++++---
Assets/Translations/uk-UA.json | 10 +++++++---
Assets/Translations/zh-CN.json | 10 +++++++---
9 files changed, 63 insertions(+), 27 deletions(-)
diff --git a/Assets/Translations/de.json b/Assets/Translations/de.json
index 6b5b58ce..534ce35c 100644
--- a/Assets/Translations/de.json
+++ b/Assets/Translations/de.json
@@ -123,9 +123,13 @@
"stream-description": "Geben Sie einen Befehl ein, der kontinuierlich ausgeführt werden soll."
},
"dynamic-text": "Dynamischer Text",
- "hide-vertical": {
- "description": "Wenn aktiviert, wird der Text aus der Befehlsausgabe nicht angezeigt, wenn sich die Leiste in einem vertikalen Layout befindet (links oder rechts).",
- "label": "Text in vertikaler Leiste ausblenden"
+ "max-text-length-horizontal": {
+ "description": "Maximale Anzahl an Zeichen, die in horizontaler Leiste angezeigt werden (0 zum Ausblenden des Textes)",
+ "label": "Max. Textlänge (horizontal)"
+ },
+ "max-text-length-vertical": {
+ "description": "Maximale Anzahl an Zeichen, die in vertikaler Leiste angezeigt werden (0 zum Ausblenden des Textes)",
+ "label": "Max. Textlänge (vertikal)"
},
"icon": {
"description": "Symbol aus der Bibliothek auswählen.",
diff --git a/Assets/Translations/es.json b/Assets/Translations/es.json
index 285ceff9..45f2c1fe 100644
--- a/Assets/Translations/es.json
+++ b/Assets/Translations/es.json
@@ -123,9 +123,13 @@
"stream-description": "Introduce un comando para ejecutar continuamente."
},
"dynamic-text": "Texto dinámico",
- "hide-vertical": {
- "description": "Si está activado, el texto de la salida del comando no se mostrará cuando la barra esté en un diseño vertical (izquierda o derecha).",
- "label": "Ocultar texto en barra vertical"
+ "max-text-length-horizontal": {
+ "description": "Número máximo de caracteres a mostrar en la barra horizontal (0 para ocultar el texto)",
+ "label": "Longitud máxima de texto (horizontal)"
+ },
+ "max-text-length-vertical": {
+ "description": "Número máximo de caracteres a mostrar en la barra vertical (0 para ocultar el texto)",
+ "label": "Longitud máxima de texto (vertical)"
},
"icon": {
"description": "Selecciona un icono de la biblioteca.",
diff --git a/Assets/Translations/fr.json b/Assets/Translations/fr.json
index fdde3a3a..0e2f7dc3 100644
--- a/Assets/Translations/fr.json
+++ b/Assets/Translations/fr.json
@@ -123,9 +123,13 @@
"stream-description": "Entrez une commande à exécuter en continu."
},
"dynamic-text": "Texte dynamique",
- "hide-vertical": {
- "description": "Si activé, le texte de la sortie de la commande ne sera pas affiché lorsque la barre est en disposition verticale (gauche ou droite).",
- "label": "Masquer le texte dans la barre verticale"
+ "max-text-length-horizontal": {
+ "description": "Nombre maximal de caractères à afficher dans la barre horizontale (0 pour masquer le texte)",
+ "label": "Longueur max du texte (horizontal)"
+ },
+ "max-text-length-vertical": {
+ "description": "Nombre maximal de caractères à afficher dans la barre verticale (0 pour masquer le texte)",
+ "label": "Longueur max du texte (vertical)"
},
"icon": {
"description": "Sélectionnez une icône dans la bibliothèque.",
diff --git a/Assets/Translations/nl.json b/Assets/Translations/nl.json
index 3b81242e..5ea3bce5 100644
--- a/Assets/Translations/nl.json
+++ b/Assets/Translations/nl.json
@@ -123,9 +123,13 @@
"stream-description": "Voer een commando in dat continu wordt uitgevoerd."
},
"dynamic-text": "Dynamische tekst",
- "hide-vertical": {
- "description": "Indien ingeschakeld wordt de tekst uit de commando-uitvoer niet getoond wanneer de balk verticaal is (links of rechts).",
- "label": "Tekst verbergen in verticale balk"
+ "max-text-length-horizontal": {
+ "description": "Maximaal aantal tekens dat moet worden weergegeven in horizontale balk (0 om tekst te verbergen)",
+ "label": "Max. tekstlengte (horizontaal)"
+ },
+ "max-text-length-vertical": {
+ "description": "Maximaal aantal tekens dat moet worden weergegeven in verticale balk (0 om tekst te verbergen)",
+ "label": "Max. tekstlengte (verticaal)"
},
"icon": {
"description": "Selecteer een pictogram uit de bibliotheek.",
diff --git a/Assets/Translations/pt.json b/Assets/Translations/pt.json
index bc1c73c9..45551a6b 100644
--- a/Assets/Translations/pt.json
+++ b/Assets/Translations/pt.json
@@ -123,9 +123,13 @@
"stream-description": "Insira um comando para executar continuamente."
},
"dynamic-text": "Texto dinâmico",
- "hide-vertical": {
- "description": "Se ativado, o texto da saída do comando não será exibido quando a barra estiver em um layout vertical (esquerda ou direita).",
- "label": "Ocultar texto na barra vertical"
+ "max-text-length-horizontal": {
+ "description": "Número máximo de caracteres a serem exibidos na barra horizontal (0 para ocultar o texto)",
+ "label": "Comprimento máximo do texto (horizontal)"
+ },
+ "max-text-length-vertical": {
+ "description": "Número máximo de caracteres a serem exibidos na barra vertical (0 para ocultar o texto)",
+ "label": "Comprimento máximo do texto (vertical)"
},
"icon": {
"description": "Selecione um ícone da biblioteca.",
diff --git a/Assets/Translations/ru.json b/Assets/Translations/ru.json
index 1b38f851..0e4d3671 100644
--- a/Assets/Translations/ru.json
+++ b/Assets/Translations/ru.json
@@ -123,9 +123,13 @@
"stream-description": "Введите команду для непрерывного выполнения."
},
"dynamic-text": "Динамический текст",
- "hide-vertical": {
- "description": "Если включено, текст из вывода команды не будет отображаться, когда панель находится в вертикальном макете (слева или справа).",
- "label": "Скрывать текст в вертикальной панели"
+ "max-text-length-horizontal": {
+ "description": "Максимальное количество символов для отображения в горизонтальной панели (0 для скрытия текста)",
+ "label": "Макс. длина текста (горизонтально)"
+ },
+ "max-text-length-vertical": {
+ "description": "Максимальное количество символов для отображения в вертикальной панели (0 для скрытия текста)",
+ "label": "Макс. длина текста (вертикально)"
},
"icon": {
"description": "Выберите иконку из библиотеки.",
diff --git a/Assets/Translations/tr.json b/Assets/Translations/tr.json
index ff31b078..87dc8672 100644
--- a/Assets/Translations/tr.json
+++ b/Assets/Translations/tr.json
@@ -123,9 +123,13 @@
"stream-description": "Sürekli çalıştırılacak bir komut girin."
},
"dynamic-text": "Dinamik metin",
- "hide-vertical": {
- "description": "Etkinleştirilirse, komut çıktısındaki metin, çubuk dikey düzende (sol veya sağ) olduğunda gösterilmeyecektir.",
- "label": "Dikey çubukta metni gizle"
+ "max-text-length-horizontal": {
+ "description": "Yatay çubukta gösterilecek maksimum karakter sayısı (metni gizlemek için 0)",
+ "label": "Maks. metin uzunluğu (yatay)"
+ },
+ "max-text-length-vertical": {
+ "description": "Dikey çubukta gösterilecek maksimum karakter sayısı (metni gizlemek için 0)",
+ "label": "Maks. metin uzunluğu (dikey)"
},
"icon": {
"description": "Kütüphaneden bir ikon seçin.",
diff --git a/Assets/Translations/uk-UA.json b/Assets/Translations/uk-UA.json
index a5b9165e..05029235 100644
--- a/Assets/Translations/uk-UA.json
+++ b/Assets/Translations/uk-UA.json
@@ -123,9 +123,13 @@
"stream-description": "Введіть команду для безперервного запуску."
},
"dynamic-text": "Динамічний текст",
- "hide-vertical": {
- "description": "Якщо увімкнено, текст з виводу команди не відображатиметься, коли панель знаходиться у вертикальному розташуванні (ліворуч або праворуч).",
- "label": "Приховати текст у вертикальній панелі"
+ "max-text-length-horizontal": {
+ "description": "Максимальна кількість символів для відображення в горизонтальній панелі (0 щоб приховати текст)",
+ "label": "Макс. довжина тексту (горизонтально)"
+ },
+ "max-text-length-vertical": {
+ "description": "Максимальна кількість символів для відображення в вертикальній панелі (0 щоб приховати текст)",
+ "label": "Макс. довжина тексту (вертикально)"
},
"icon": {
"description": "Вибрати значок з бібліотеки.",
diff --git a/Assets/Translations/zh-CN.json b/Assets/Translations/zh-CN.json
index ec203be5..c2b15781 100644
--- a/Assets/Translations/zh-CN.json
+++ b/Assets/Translations/zh-CN.json
@@ -123,9 +123,13 @@
"stream-description": "输入一个要持续运行的命令。"
},
"dynamic-text": "动态文本",
- "hide-vertical": {
- "description": "如果启用,当栏处于垂直布局(左或右)时,将不显示命令输出的文本。",
- "label": "在垂直栏中隐藏文本"
+ "max-text-length-horizontal": {
+ "description": "在水平栏中显示的最大字符数(0 为隐藏文本)",
+ "label": "最大文本长度(水平)"
+ },
+ "max-text-length-vertical": {
+ "description": "在垂直栏中显示的最大字符数(0 为隐藏文本)",
+ "label": "最大文本长度(垂直)"
},
"icon": {
"description": "从库中选择图标。",
From 694fefeebdb712fa5071369f149d4edfcbfec3f9 Mon Sep 17 00:00:00 2001
From: loner <2788892716@qq.com>
Date: Fri, 21 Nov 2025 10:58:15 +0800
Subject: [PATCH 6/9] feat: Custom buttons now support wheel actions
---
Assets/Translations/en.json | 17 +++
Modules/Bar/Widgets/CustomButton.qml | 99 ++++++++++++++++-
.../WidgetSettings/CustomButtonSettings.qml | 104 ++++++++++++++++++
Services/UI/BarWidgetRegistry.qml | 7 ++
4 files changed, 226 insertions(+), 1 deletion(-)
diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json
index ec385399..cfebc8b5 100644
--- a/Assets/Translations/en.json
+++ b/Assets/Translations/en.json
@@ -145,6 +145,23 @@
"label": "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": {
"description": "Parse the command output as a JSON object to dynamically set text and icon.",
"label": "Parse output as JSON"
diff --git a/Modules/Bar/Widgets/CustomButton.qml b/Modules/Bar/Widgets/CustomButton.qml
index e73bad54..e4a9586a 100644
--- a/Modules/Bar/Widgets/CustomButton.qml
+++ b/Modules/Bar/Widgets/CustomButton.qml
@@ -39,12 +39,21 @@ Item {
readonly property bool rightClickUpdateText: widgetSettings.rightClickUpdateText ?? widgetMetadata.rightClickUpdateText
readonly property string middleClickExec: widgetSettings.middleClickExec || widgetMetadata.middleClickExec
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 bool textStream: widgetSettings.textStream !== undefined ? widgetSettings.textStream : (widgetMetadata.textStream || false)
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 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
implicitHeight: pill.height
@@ -73,6 +82,16 @@ Item {
if (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 !== "") {
@@ -92,6 +111,7 @@ Item {
onClicked: root.onClicked()
onRightClicked: root.onRightClicked()
onMiddleClicked: root.onMiddleClicked()
+ onWheel: delta => root.onWheel(delta)
}
// Internal state for dynamic text
@@ -382,4 +402,81 @@ Item {
textProc.command = ["sh", "-lc", textCommand];
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()
+ }
+ }
+ }
+ }
}
diff --git a/Modules/Panels/Settings/Bar/WidgetSettings/CustomButtonSettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/CustomButtonSettings.qml
index e209471b..4d7a7d3b 100644
--- a/Modules/Panels/Settings/Bar/WidgetSettings/CustomButtonSettings.qml
+++ b/Modules/Panels/Settings/Bar/WidgetSettings/CustomButtonSettings.qml
@@ -28,6 +28,13 @@ ColumnLayout {
settings.rightClickUpdateText = rightClickUpdateText.checked;
settings.middleClickExec = middleClickExecInput.text;
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.textCollapse = textCollapseInput.text;
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 {
Layout.fillWidth: true
}
diff --git a/Services/UI/BarWidgetRegistry.qml b/Services/UI/BarWidgetRegistry.qml
index f9423f68..456e5be3 100644
--- a/Services/UI/BarWidgetRegistry.qml
+++ b/Services/UI/BarWidgetRegistry.qml
@@ -124,6 +124,13 @@ Singleton {
"textIntervalMs": 3000,
"textCollapse": "",
"parseJson": false,
+ "wheelExec": "",
+ "wheelUpExec": "",
+ "wheelDownExec": "",
+ "wheelMode": "unified",
+ "wheelUpdateText": false,
+ "wheelUpUpdateText": false,
+ "wheelDownUpdateText": false,
"maxTextLength": {
"horizontal": 10,
"vertical": 10
From e8a27acb630f8cbbb5ce5a5673d3efe1c36b9181 Mon Sep 17 00:00:00 2001
From: loner <2788892716@qq.com>
Date: Fri, 21 Nov 2025 11:06:51 +0800
Subject: [PATCH 7/9] fix: Left click behavior should only depend on left click
settings
---
Modules/Bar/Widgets/CustomButton.qml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Modules/Bar/Widgets/CustomButton.qml b/Modules/Bar/Widgets/CustomButton.qml
index e4a9586a..6cd7a702 100644
--- a/Modules/Bar/Widgets/CustomButton.qml
+++ b/Modules/Bar/Widgets/CustomButton.qml
@@ -341,8 +341,8 @@ Item {
if (leftClickExec) {
Quickshell.execDetached(["sh", "-c", leftClickExec]);
Logger.i("CustomButton", `Executing command: ${leftClickExec}`);
- } else if (!hasExec && !leftClickUpdateText) {
- // No script was defined, open settings
+ } else if (!leftClickUpdateText) {
+ // No left click script was defined, open settings
var settingsPanel = PanelService.getPanel("settingsPanel", screen);
settingsPanel.requestedTab = SettingsPanel.Tab.Bar;
settingsPanel.open();
From f7d7d7ac15dcf223fc80ac73b997c0e4a64d62f8 Mon Sep 17 00:00:00 2001
From: loner <2788892716@qq.com>
Date: Fri, 21 Nov 2025 12:38:50 +0800
Subject: [PATCH 8/9] fix: Stabilize custom button wheel command settings UI
layout
---
.../Settings/Bar/WidgetSettings/CustomButtonSettings.qml | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/Modules/Panels/Settings/Bar/WidgetSettings/CustomButtonSettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/CustomButtonSettings.qml
index 4d7a7d3b..b498784d 100644
--- a/Modules/Panels/Settings/Bar/WidgetSettings/CustomButtonSettings.qml
+++ b/Modules/Panels/Settings/Bar/WidgetSettings/CustomButtonSettings.qml
@@ -163,6 +163,7 @@ ColumnLayout {
ColumnLayout {
Layout.fillWidth: true
+ Layout.preferredWidth: parent.width
RowLayout {
id: unifiedWheelLayout
@@ -191,9 +192,9 @@ ColumnLayout {
}
ColumnLayout {
- id: separateWheelLayout
+ id: separatedWheelLayout
+ Layout.fillWidth: true
visible: separateWheelToggle.checked
- spacing: Style.marginS
RowLayout {
spacing: Style.marginM
From ee33da8348b36977dcc5d6d76169696a5c19263f Mon Sep 17 00:00:00 2001
From: loner <2788892716@qq.com>
Date: Fri, 21 Nov 2025 13:14:57 +0800
Subject: [PATCH 9/9] i18n: fix: Add translations for custom button wheel
actions
---
Assets/Translations/de.json | 17 +++++++++++++++++
Assets/Translations/es.json | 17 +++++++++++++++++
Assets/Translations/fr.json | 17 +++++++++++++++++
Assets/Translations/nl.json | 17 +++++++++++++++++
Assets/Translations/pt.json | 17 +++++++++++++++++
Assets/Translations/ru.json | 17 +++++++++++++++++
Assets/Translations/tr.json | 17 +++++++++++++++++
Assets/Translations/uk-UA.json | 17 +++++++++++++++++
Assets/Translations/zh-CN.json | 17 +++++++++++++++++
9 files changed, 153 insertions(+)
diff --git a/Assets/Translations/de.json b/Assets/Translations/de.json
index 534ce35c..917f305b 100644
--- a/Assets/Translations/de.json
+++ b/Assets/Translations/de.json
@@ -145,6 +145,23 @@
"label": "Mittelklick",
"update-text": "Text auf Mittelklick aktualisieren"
},
+ "wheel": {
+ "description": "Befehl, der bei Verwendung des Scrollrads ausgeführt wird.\nVerwenden Sie $delta für die Scrollrad-Delta im Befehl",
+ "label": "Scrollrad",
+ "update-text": "Anzeigetext beim Scrollen aktualisieren"
+ },
+ "wheel-mode-separate": {
+ "label": "Separate Scrollrad-Befehle",
+ "description": "Separate Befehle für Scrollrad hoch und runter aktivieren"
+ },
+ "wheel-up": {
+ "description": "Befehl, der ausgeführt wird, wenn das Scrollrad hochgescrollt wird.",
+ "label": "Scrollrad hoch Befehl"
+ },
+ "wheel-down": {
+ "description": "Befehl, der ausgeführt wird, wenn das Scrollrad heruntergescrollt wird.",
+ "label": "Scrollrad runter Befehl"
+ },
"parse-json": {
"description": "Die Befehlsausgabe als JSON-Objekt parsen, um Text und Symbol dynamisch festzulegen.",
"label": "Ausgabe als JSON parsen"
diff --git a/Assets/Translations/es.json b/Assets/Translations/es.json
index 45f2c1fe..31f4e95a 100644
--- a/Assets/Translations/es.json
+++ b/Assets/Translations/es.json
@@ -145,6 +145,23 @@
"label": "Clic medio",
"update-text": "Actualizar el texto mostrado al hacer clic con el botón central"
},
+ "wheel": {
+ "description": "Comando a ejecutar cuando se usa la rueda de desplazamiento.\nUsa $delta para el delta de la rueda de desplazamiento en el comando",
+ "label": "Rueda de desplazamiento",
+ "update-text": "Actualizar texto mostrado al desplazarse"
+ },
+ "wheel-mode-separate": {
+ "label": "Comandos de rueda separados",
+ "description": "Habilitar comandos separados para rueda arriba y abajo"
+ },
+ "wheel-up": {
+ "description": "Comando a ejecutar cuando la rueda de desplazamiento se desplaza hacia arriba.",
+ "label": "Comando de rueda hacia arriba"
+ },
+ "wheel-down": {
+ "description": "Comando a ejecutar cuando la rueda de desplazamiento se desplaza hacia abajo.",
+ "label": "Comando de rueda hacia abajo"
+ },
"parse-json": {
"description": "Analizar la salida del comando como un objeto JSON para establecer dinámicamente el texto y el ícono.",
"label": "Analizar salida como JSON"
diff --git a/Assets/Translations/fr.json b/Assets/Translations/fr.json
index 0e2f7dc3..3a532024 100644
--- a/Assets/Translations/fr.json
+++ b/Assets/Translations/fr.json
@@ -145,6 +145,23 @@
"label": "Clic milieu",
"update-text": "Mettre à jour le texte affiché lors d'un clic molette."
},
+ "wheel": {
+ "description": "Commande à exécuter lorsque la molette est utilisée.\nUtilisez $delta pour le delta de la molette dans la commande",
+ "label": "Molette",
+ "update-text": "Mettre à jour le texte affiché au défilement"
+ },
+ "wheel-mode-separate": {
+ "label": "Commandes de molette séparées",
+ "description": "Activer des commandes séparées pour la molette haut et bas"
+ },
+ "wheel-up": {
+ "description": "Commande à exécuter lorsque la molette est défilée vers le haut.",
+ "label": "Commande molette haut"
+ },
+ "wheel-down": {
+ "description": "Commande à exécuter lorsque la molette est défilée vers le bas.",
+ "label": "Commande molette bas"
+ },
"parse-json": {
"description": "Analyser la sortie de la commande en tant qu'objet JSON pour définir dynamiquement le texte et l'icône.",
"label": "Analyser la sortie en JSON"
diff --git a/Assets/Translations/nl.json b/Assets/Translations/nl.json
index 5ea3bce5..41dfba10 100644
--- a/Assets/Translations/nl.json
+++ b/Assets/Translations/nl.json
@@ -145,6 +145,23 @@
"label": "Middelste muisklik",
"update-text": "Tekst bijwerken bij middelste muisklik"
},
+ "wheel": {
+ "description": "Commando om uit te voeren wanneer het scrollwiel wordt gebruikt.\nGebruik $delta voor de scrollwiel-delta in het commando",
+ "label": "Scrollwiel",
+ "update-text": "Weergegeven tekst bij scrollen bijwerken"
+ },
+ "wheel-mode-separate": {
+ "label": "Afzonderlijke scrollwielcommando's",
+ "description": "Afzonderlijke commando's inschakelen voor scrollwiel omhoog en omlaag"
+ },
+ "wheel-up": {
+ "description": "Commando om uit te voeren wanneer het scrollwiel omhoog wordt bewogen.",
+ "label": "Scrollwiel omhoog commando"
+ },
+ "wheel-down": {
+ "description": "Commando om uit te voeren wanneer het scrollwiel omlaag wordt bewogen.",
+ "label": "Scrollwiel omlaag commando"
+ },
"parse-json": {
"description": "Interpreteer de commando-uitvoer als JSON-object om tekst en pictogram dynamisch in te stellen.",
"label": "Uitvoer als JSON parseren"
diff --git a/Assets/Translations/pt.json b/Assets/Translations/pt.json
index 45551a6b..42f28ac9 100644
--- a/Assets/Translations/pt.json
+++ b/Assets/Translations/pt.json
@@ -145,6 +145,23 @@
"label": "Clique do meio",
"update-text": "Atualizar texto exibido com clique do meio"
},
+ "wheel": {
+ "description": "Comando a executar quando a roda de rolagem é usada.\nUse $delta para o delta da roda de rolagem no comando",
+ "label": "Roda de rolagem",
+ "update-text": "Atualizar texto exibido ao rolar"
+ },
+ "wheel-mode-separate": {
+ "label": "Comandos de roda separados",
+ "description": "Ativar comandos separados para roda para cima e para baixo"
+ },
+ "wheel-up": {
+ "description": "Comando a executar quando a roda de rolagem é rolada para cima.",
+ "label": "Comando de roda para cima"
+ },
+ "wheel-down": {
+ "description": "Comando a executar quando a roda de rolagem é rolada para baixo.",
+ "label": "Comando de roda para baixo"
+ },
"parse-json": {
"description": "Analisa a saída do comando como um objeto JSON para definir dinamicamente o texto e o ícone.",
"label": "Analisar saída como JSON"
diff --git a/Assets/Translations/ru.json b/Assets/Translations/ru.json
index 0e4d3671..094cf09c 100644
--- a/Assets/Translations/ru.json
+++ b/Assets/Translations/ru.json
@@ -145,6 +145,23 @@
"label": "Клик средней кнопкой",
"update-text": "Обновить отображаемый текст по среднему клику"
},
+ "wheel": {
+ "description": "Команда для выполнения при использовании колеса прокрутки.\nИспользуйте $delta для дельты колеса прокрутки в команде",
+ "label": "Колесо прокрутки",
+ "update-text": "Обновить отображаемый текст при прокрутке"
+ },
+ "wheel-mode-separate": {
+ "label": "Раздельные команды колеса прокрутки",
+ "description": "Включить раздельные команды для колеса прокрутки вверх и вниз"
+ },
+ "wheel-up": {
+ "description": "Команда для выполнения при прокрутке колеса вверх.",
+ "label": "Команда прокрутки колеса вверх"
+ },
+ "wheel-down": {
+ "description": "Команда для выполнения при прокрутке колеса вниз.",
+ "label": "Команда прокрутки колеса вниз"
+ },
"parse-json": {
"description": "Разобрать вывод команды как объект JSON для динамической установки текста и иконки.",
"label": "Разобрать вывод как JSON"
diff --git a/Assets/Translations/tr.json b/Assets/Translations/tr.json
index 87dc8672..1fb05300 100644
--- a/Assets/Translations/tr.json
+++ b/Assets/Translations/tr.json
@@ -145,6 +145,23 @@
"label": "Orta tıklama",
"update-text": "Orta tıklamayla görüntülenen metni güncelle"
},
+ "wheel": {
+ "description": "Kaydırma tekerleği kullanıldığında yürütülecek komut.\nKomutta kaydırma tekerleği deltası için $delta kullanın",
+ "label": "Kaydırma tekerleği",
+ "update-text": "Kaydırmada gösterilen metni güncelle"
+ },
+ "wheel-mode-separate": {
+ "label": "Ayrı kaydırma tekerleği komutları",
+ "description": "Kaydırma tekerleği yukarı ve aşağı için ayrı komutları etkinleştir"
+ },
+ "wheel-up": {
+ "description": "Kaydırma tekerleği yukarı kaydırıldığında yürütülecek komut.",
+ "label": "Kaydırma tekerleği yukarı komutu"
+ },
+ "wheel-down": {
+ "description": "Kaydırma tekerleği aşağı kaydırıldığında yürütülecek komut.",
+ "label": "Kaydırma tekerleği aşağı komutu"
+ },
"parse-json": {
"description": "Komut çıktısını metin ve ikon dinamik olarak ayarlamak için bir JSON nesnesi olarak ayrıştırın.",
"label": "Çıktıyı JSON olarak ayrıştır"
diff --git a/Assets/Translations/uk-UA.json b/Assets/Translations/uk-UA.json
index 05029235..8e20b116 100644
--- a/Assets/Translations/uk-UA.json
+++ b/Assets/Translations/uk-UA.json
@@ -145,6 +145,23 @@
"label": "Середній клік",
"update-text": "Оновити відображуваний текст при натисканні середньою кнопкою миші"
},
+ "wheel": {
+ "description": "Команда для виконання при використанні колеса прокрутки.\nВикористовуйте $delta для дельти колеса прокрутки в команді",
+ "label": "Колесо прокрутки",
+ "update-text": "Оновити відображуваний текст при прокрутці"
+ },
+ "wheel-mode-separate": {
+ "label": "Окремі команди колеса прокрутки",
+ "description": "Увімкнути окремі команди для колеса прокрутки вгору та вниз"
+ },
+ "wheel-up": {
+ "description": "Команда для виконання при прокрутці колеса вгору.",
+ "label": "Команда прокрутки колеса вгору"
+ },
+ "wheel-down": {
+ "description": "Команда для виконання при прокрутці колеса вниз.",
+ "label": "Команда прокрутки колеса вниз"
+ },
"parse-json": {
"description": "Розбирати виведення команди як JSON-об'єкт для динамічного встановлення тексту та значка.",
"label": "Розбирати виведення як JSON"
diff --git a/Assets/Translations/zh-CN.json b/Assets/Translations/zh-CN.json
index c2b15781..1ebb919b 100644
--- a/Assets/Translations/zh-CN.json
+++ b/Assets/Translations/zh-CN.json
@@ -145,6 +145,23 @@
"label": "中键点击",
"update-text": "鼠标中键点击时更新显示的文本"
},
+ "wheel": {
+ "description": "使用滚轮时执行的命令。\n在命令中使用 $delta 表示滚轮增量",
+ "label": "滚轮",
+ "update-text": "滚轮滚动时更新显示的文本"
+ },
+ "wheel-mode-separate": {
+ "label": "分开滚轮命令",
+ "description": "为滚轮向上和向下启用单独的命令"
+ },
+ "wheel-up": {
+ "description": "滚轮向上滚动时执行的命令。",
+ "label": "滚轮向上命令"
+ },
+ "wheel-down": {
+ "description": "滚轮向下滚动时执行的命令。",
+ "label": "滚轮向下命令"
+ },
"parse-json": {
"description": "将命令输出解析为 JSON 对象,以动态设置文本和图标。",
"label": "将输出解析为 JSON"