diff --git a/Assets/Translations/de.json b/Assets/Translations/de.json index 823bedd6..d116b8ce 100644 --- a/Assets/Translations/de.json +++ b/Assets/Translations/de.json @@ -123,6 +123,10 @@ "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" + }, "icon": { "description": "Symbol aus der Bibliothek auswählen.", "label": "Symbol" diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index 599e4afd..3197e2bd 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -123,6 +123,10 @@ "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" + }, "icon": { "description": "Select an icon from the library.", "label": "Icon" diff --git a/Assets/Translations/es.json b/Assets/Translations/es.json index 884938bc..16677a9d 100644 --- a/Assets/Translations/es.json +++ b/Assets/Translations/es.json @@ -123,6 +123,10 @@ "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" + }, "icon": { "description": "Selecciona un icono de la biblioteca.", "label": "Icono" diff --git a/Assets/Translations/fr.json b/Assets/Translations/fr.json index 3ac75897..17facb56 100644 --- a/Assets/Translations/fr.json +++ b/Assets/Translations/fr.json @@ -123,6 +123,10 @@ "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" + }, "icon": { "description": "Sélectionnez une icône dans la bibliothèque.", "label": "Icône" diff --git a/Assets/Translations/pt.json b/Assets/Translations/pt.json index 8b63b951..46e3666f 100644 --- a/Assets/Translations/pt.json +++ b/Assets/Translations/pt.json @@ -123,6 +123,10 @@ "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" + }, "icon": { "description": "Selecione um ícone da biblioteca.", "label": "Ícone" diff --git a/Assets/Translations/tr.json b/Assets/Translations/tr.json index 93dfa869..01e237db 100644 --- a/Assets/Translations/tr.json +++ b/Assets/Translations/tr.json @@ -123,6 +123,10 @@ "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" + }, "icon": { "description": "Kütüphaneden bir ikon seçin.", "label": "İkon" diff --git a/Assets/Translations/uk-UA.json b/Assets/Translations/uk-UA.json index b522d3ec..3a16c814 100644 --- a/Assets/Translations/uk-UA.json +++ b/Assets/Translations/uk-UA.json @@ -123,6 +123,10 @@ "stream-description": "Введіть команду для безперервного запуску." }, "dynamic-text": "Динамічний текст", + "hide-vertical": { + "description": "Якщо увімкнено, текст з виводу команди не відображатиметься, коли панель знаходиться у вертикальному розташуванні (ліворуч або праворуч).", + "label": "Приховати текст у вертикальній панелі" + }, "icon": { "description": "Вибрати значок з бібліотеки.", "label": "Значок" diff --git a/Assets/Translations/zh-CN.json b/Assets/Translations/zh-CN.json index 93faf8bf..ab79009d 100644 --- a/Assets/Translations/zh-CN.json +++ b/Assets/Translations/zh-CN.json @@ -123,6 +123,10 @@ "stream-description": "输入一个要持续运行的命令。" }, "dynamic-text": "动态文本", + "hide-vertical": { + "description": "如果启用,当栏处于垂直布局(左或右)时,将不显示命令输出的文本。", + "label": "在垂直栏中隐藏文本" + }, "icon": { "description": "从库中选择图标。", "label": "图标" diff --git a/Modules/Bar/Extras/BarPill.qml b/Modules/Bar/Extras/BarPill.qml index d5befb21..a8c097cf 100644 --- a/Modules/Bar/Extras/BarPill.qml +++ b/Modules/Bar/Extras/BarPill.qml @@ -17,6 +17,7 @@ Item { property bool forceClose: false property bool oppositeDirection: false property bool hovered: false + property bool rotateText: false readonly property string barPosition: Settings.data.bar.position readonly property bool isVerticalBar: barPosition === "left" || barPosition === "right" @@ -52,6 +53,7 @@ Item { oppositeDirection: root.oppositeDirection hovered: root.hovered density: root.density + rotateText: root.rotateText onShown: root.shown() onHidden: root.hidden() onEntered: root.entered() diff --git a/Modules/Bar/Extras/BarPillVertical.qml b/Modules/Bar/Extras/BarPillVertical.qml index ea1001a7..ecd170f1 100644 --- a/Modules/Bar/Extras/BarPillVertical.qml +++ b/Modules/Bar/Extras/BarPillVertical.qml @@ -18,6 +18,7 @@ Item { property bool forceClose: false property bool oppositeDirection: false property bool hovered: false + property bool rotateText: false // Bar position detection for pill direction readonly property string barPosition: Settings.data.bar.position @@ -48,8 +49,8 @@ Item { 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: buttonSize - readonly property int maxPillHeight: Math.max(1, Math.round(textItem.implicitHeight + pillPaddingVertical * 4)) + 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 real iconSize: { switch (root.density) { @@ -109,14 +110,8 @@ Item { id: textItem anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: { - var offset = openDownward ? Math.round(pillPaddingVertical * 0.75) : -Math.round(pillPaddingVertical * 0.75) - if (forceOpen) { - // If its force open, the icon disc background is the same color as the bg pill move text slightly - offset += oppositeDirection ? -Style.marginXXS : Style.marginXXS - } - return offset - } + anchors.verticalCenterOffset: rotateText ? Math.round(iconCircle.height / 4) : getVerticalCenterOffset() + rotation: rotateText ? -90 : 0 text: root.text + root.suffix family: Settings.data.ui.fontFixed pointSize: textSize @@ -126,8 +121,15 @@ Item { verticalAlignment: Text.AlignVCenter color: forceOpen ? Color.mOnSurface : Color.mPrimary 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 + } + } Behavior on width { enabled: showAnim.running || hideAnim.running NumberAnimation { diff --git a/Modules/Bar/Widgets/CustomButton.qml b/Modules/Bar/Widgets/CustomButton.qml index 7e474fde..3e67fb0c 100644 --- a/Modules/Bar/Widgets/CustomButton.qml +++ b/Modules/Bar/Widgets/CustomButton.qml @@ -30,7 +30,8 @@ Item { return {} } - // Use settings or defaults from BarWidgetRegistry + readonly property bool isVerticalBar: Settings.data.bar.position === "left" || Settings.data.bar.position === "right" + readonly property string customIcon: widgetSettings.icon || widgetMetadata.icon readonly property string leftClickExec: widgetSettings.leftClickExec || widgetMetadata.leftClickExec readonly property string rightClickExec: widgetSettings.rightClickExec || widgetMetadata.rightClickExec @@ -40,8 +41,11 @@ 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 @@ -50,25 +54,37 @@ Item { oppositeDirection: BarService.getPillDirection(root) icon: _dynamicIcon !== "" ? _dynamicIcon : customIcon - text: _dynamicText + text: shouldShowText ? _dynamicText : "" density: Settings.data.bar.density + rotateText: isVerticalBar && !hideTextInVerticalBar autoHide: false forceOpen: _dynamicText !== "" tooltipText: { - if (!hasExec) { - return "Custom button, configure in settings." - } else { - var lines = [] + var tooltipLines = [] + + if (hasExec) { if (leftClickExec !== "") { - lines.push(`Left click: ${leftClickExec}.`) + tooltipLines.push(`Left click: ${leftClickExec}.`) } if (rightClickExec !== "") { - lines.push(`Right click: ${rightClickExec}.`) + tooltipLines.push(`Right click: ${rightClickExec}.`) } if (middleClickExec !== "") { - lines.push(`Middle click: ${middleClickExec}.`) + tooltipLines.push(`Middle click: ${middleClickExec}.`) } - return lines.join("\n") + } + + if (_dynamicTooltip !== "") { + if (tooltipLines.length > 0) { + tooltipLines.push("") + } + tooltipLines.push(_dynamicTooltip) + } + + if (tooltipLines.length === 0) { + return "Custom button, configure in settings." + } else { + return tooltipLines.join("\n") } } @@ -80,13 +96,14 @@ Item { // Internal state for dynamic text property string _dynamicText: "" property string _dynamicIcon: "" + property string _dynamicTooltip: "" // Periodically run the text command (if set) Timer { id: refreshTimer interval: Math.max(250, textIntervalMs) repeat: true - running: !textStream && textCommand && textCommand.length > 0 + running: shouldShowText && !textStream && textCommand && textCommand.length > 0 triggeredOnStart: true onTriggered: root.runTextCommand() } @@ -95,7 +112,7 @@ Item { Timer { id: restartTimer interval: 1000 - running: textStream && !textProc.running + running: shouldShowText && textStream && !textProc.running onTriggered: root.runTextCommand() } @@ -123,38 +140,49 @@ Item { function parseDynamicContent(content) { var contentStr = String(content || "").trim() - if (contentStr.indexOf("\n") !== -1) { - contentStr = contentStr.split("\n")[0] - } if (parseJson) { + var lineToParse = contentStr + + if (!textStream && contentStr.includes('\n')) { + const lines = contentStr.split('\n').filter(line => line.trim() !== '') + if (lines.length > 0) { + lineToParse = lines[lines.length - 1] + } + } + try { - var parsed = JSON.parse(contentStr) - var text = parsed.text || "" + const parsed = JSON.parse(lineToParse) + const text = parsed.text || "" + const icon = parsed.icon || "" + const tooltip = parsed.tooltip || "" if (checkCollapse(text)) { _dynamicText = "" _dynamicIcon = "" + _dynamicTooltip = "" return } _dynamicText = text - _dynamicIcon = parsed.icon || "" + _dynamicIcon = icon + _dynamicTooltip = tooltip return } catch (e) { - - // Not a valid JSON, treat as plain text + Logger.w("CustomButton", `Failed to parse JSON. Content: "${lineToParse}"`) } } if (checkCollapse(contentStr)) { _dynamicText = "" _dynamicIcon = "" + _dynamicTooltip = "" return } _dynamicText = contentStr _dynamicIcon = "" + _dynamicTooltip = "" } function checkCollapse(text) { diff --git a/Modules/Panels/Settings/Bar/WidgetSettings/CustomButtonSettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/CustomButtonSettings.qml index 5d11c8de..715e4ef9 100644 --- a/Modules/Panels/Settings/Bar/WidgetSettings/CustomButtonSettings.qml +++ b/Modules/Panels/Settings/Bar/WidgetSettings/CustomButtonSettings.qml @@ -15,6 +15,7 @@ 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 function saveSettings() { var settings = Object.assign({}, widgetData || {}) @@ -26,6 +27,7 @@ ColumnLayout { settings.textCollapse = textCollapseInput.text settings.textStream = valueTextStream settings.parseJson = valueParseJson + settings.hideTextInVerticalBar = valueHideTextInVerticalBar settings.textIntervalMs = parseInt(textIntervalInput.text || textIntervalInput.placeholderText, 10) return settings } @@ -94,6 +96,13 @@ 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 + } + NToggle { id: textStreamInput label: I18n.tr("bar.widget-settings.custom-button.text-stream.label") diff --git a/Services/UI/BarWidgetRegistry.qml b/Services/UI/BarWidgetRegistry.qml index 18aeb382..5b0c8ffa 100644 --- a/Services/UI/BarWidgetRegistry.qml +++ b/Services/UI/BarWidgetRegistry.qml @@ -99,7 +99,8 @@ Singleton { "textStream": false, "textIntervalMs": 3000, "textCollapse": "", - "parseJson": false + "parseJson": false, + "hideTextInVerticalBar": false }, "KeyboardLayout": { "allowUserSettings": true,