feat: add showIcon and hideMode options for CustomButton

This commit is contained in:
loner
2025-12-06 11:41:29 +08:00
parent 554e911117
commit 432936f58f
6 changed files with 159 additions and 11 deletions
+11
View File
@@ -131,11 +131,22 @@
"label": "Display command output",
"stream-description": "Enter a command to run continuously."
},
"hide-mode": {
"description": "Controls widget visibility when the command has no output.",
"label": "Hide mode",
"expandWithOutput": "Expand when has output",
"alwaysExpanded": "Always expanded",
"maxTransparent": "Max expanded but transparent"
},
"dynamic-text": "Dynamic text",
"icon": {
"description": "Select an icon from the library.",
"label": "Icon"
},
"show-icon": {
"description": "Toggles the visibility of the widget's icon.",
"label": "Show icon"
},
"left-click": {
"description": "Command to execute when the button is left-clicked.",
"label": "Left click",
+16 -4
View File
@@ -27,6 +27,7 @@ Item {
// Effective shown state (true if hovered/animated open or forced)
readonly property bool revealed: !forceClose && (forceOpen || showPill)
readonly property bool hasIcon: root.icon !== ""
signal shown
signal hidden
@@ -68,7 +69,14 @@ Item {
}
}
width: collapseToIcon ? pillHeight : pillHeight + Math.max(0, pill.width - pillOverlap)
width: {
if (collapseToIcon) {
return hasIcon ? pillHeight : 0;
}
var overlap = hasIcon ? pillOverlap : 0;
var baseWidth = hasIcon ? pillHeight : 0;
return baseWidth + Math.max(0, pill.width - overlap);
}
height: pillHeight
Connections {
@@ -103,8 +111,10 @@ Item {
width: revealed ? pillMaxWidth : 1
height: pillHeight
x: oppositeDirection ? (iconCircle.x + iconCircle.width / 2) : // Opens right
(iconCircle.x + iconCircle.width / 2) - width // Opens left
x: {
if (!hasIcon) return 0;
return oppositeDirection ? (iconCircle.x + iconCircle.width / 2) : (iconCircle.x + iconCircle.width / 2) - width;
}
opacity: revealed ? Style.opacityFull : Style.opacityNone
color: Color.transparent // Make pill background transparent to avoid double opacity
@@ -119,6 +129,8 @@ Item {
id: textItem
anchors.verticalCenter: parent.verticalCenter
x: {
if (!hasIcon) return (parent.width - width) / 2;
// Better text horizontal centering
var centerX = (parent.width - width) / 2;
var offset = oppositeDirection ? Style.marginXS : -Style.marginXS;
@@ -155,7 +167,7 @@ Item {
Rectangle {
id: iconCircle
width: pillHeight
width: hasIcon ? pillHeight : 0
height: pillHeight
radius: Math.min(Style.radiusL, width / 2)
color: Color.transparent // Make icon background transparent to avoid double opacity
+21 -4
View File
@@ -52,6 +52,7 @@ Item {
// Effective shown state (true if animated open or forced, but not if force closed)
readonly property bool revealed: !forceClose && (forceOpen || showPill)
readonly property bool hasIcon: root.icon !== ""
// Always prioritize hover color, then the custom one and finally the fallback color
readonly property color bgColor: hovered ? Color.mHover : (customBackgroundColor.a > 0) ? customBackgroundColor : Style.capsuleColor
@@ -77,7 +78,18 @@ Item {
// For vertical bars: width is just icon size, height includes pill space
width: buttonSize
height: collapseToIcon ? buttonSize : (revealed ? (buttonSize + maxPillHeight - pillOverlap) : buttonSize)
height: {
if (collapseToIcon) {
return hasIcon ? buttonSize : 0
}
if (revealed) {
var overlap = hasIcon ? pillOverlap : 0
var baseHeight = hasIcon ? buttonSize : 0
return baseHeight + Math.max(0, maxPillHeight - overlap)
}
// Fallback to buttonSize in idle state to remain clickable
return buttonSize
}
Connections {
target: root
@@ -92,9 +104,11 @@ Item {
Rectangle {
id: pillBackground
width: buttonSize
height: collapseToIcon ? buttonSize : (revealed ? (buttonSize + maxPillHeight - pillOverlap) : buttonSize)
height: root.height
radius: Style.radiusM
color: root.bgColor
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
Behavior on color {
ColorAnimation {
@@ -112,7 +126,10 @@ Item {
// Position based on direction - center the pill relative to the icon
x: 0
y: openUpward ? (iconCircle.y + iconCircle.height / 2 - height) : (iconCircle.y + iconCircle.height / 2)
y: {
if (!hasIcon) return 0;
return openUpward ? (iconCircle.y + iconCircle.height / 2 - height) : (iconCircle.y + iconCircle.height / 2);
}
opacity: revealed ? Style.opacityFull : Style.opacityNone
color: Color.transparent // Make pill background transparent to avoid double opacity
@@ -129,7 +146,7 @@ Item {
id: textItem
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: openDownward ? Style.marginXXS : -Style.marginXXS
anchors.verticalCenterOffset: hasIcon ? (openDownward ? Style.marginXXS : -Style.marginXXS) : 0
rotation: rotateText ? -90 : 0
text: root.text + root.suffix
family: Settings.data.ui.fontFixed
+82 -3
View File
@@ -52,6 +52,82 @@ Item {
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 || (wheelMode === "unified" && wheelExec) || (wheelMode === "separate" && (wheelUpExec || wheelDownExec)))
readonly property bool showIcon: (widgetSettings.showIcon !== undefined) ? widgetSettings.showIcon : true
readonly property string hideMode: widgetSettings.hideMode || "alwaysExpanded"
readonly property bool hasOutput: _dynamicText !== ""
readonly property bool shouldForceOpen: textStream && (hideMode === "alwaysExpanded" || hideMode === "maxTransparent")
readonly property bool _useNewHideLogic: textCommand && textCommand.length > 0 && textStream
readonly property bool _useTextCommandLogic: textCommand && textCommand.length > 0 && textStream
readonly property bool _pillVisible: {
if (!_useTextCommandLogic) {
return true;
}
if (hideMode === "alwaysExpanded" || hideMode === "maxTransparent") {
return true;
}
var hasActualIcon = (_dynamicIcon !== "" || customIcon !== "");
return hasOutput || (showIcon && hasActualIcon);
}
readonly property real _pillOpacity: {
if (!_useTextCommandLogic) {
return 1.0;
}
if (hideMode === "maxTransparent" && !hasOutput) {
return 0.0;
}
return 1.0;
}
readonly property bool _pillForceOpen: {
if (!_useTextCommandLogic) {
return _dynamicText !== "" || (textStream && currentMaxTextLength > 0);
}
if (currentMaxTextLength <= 0) {
return false;
}
if (hideMode === "alwaysExpanded" || hideMode === "maxTransparent") {
return true;
}
return hasOutput;
}
readonly property string _pillIcon: {
if (!_useTextCommandLogic) {
if (textCommand && textCommand.length > 0 && showIcon) {
return _dynamicIcon !== "" ? _dynamicIcon : customIcon;
} else if (!(textCommand && textCommand.length > 0)) {
return _dynamicIcon !== "" ? _dynamicIcon : customIcon;
} else {
return "";
}
}
if (!showIcon) return "";
var actualIcon = _dynamicIcon !== "" ? _dynamicIcon : customIcon;
if (hideMode === "expandWithOutput" && actualIcon === "") {
return "question-mark";
}
return actualIcon;
}
readonly property string _pillText: {
if (!_useTextCommandLogic) {
return (!isVerticalBar || currentMaxTextLength > 0) ? _dynamicText : "";
}
if (currentMaxTextLength <= 0) {
return "";
}
if (hasOutput) {
return _dynamicText;
}
if (hideMode === "expandWithOutput") {
return "";
}
return " ".repeat(currentMaxTextLength);
}
implicitWidth: pill.width
implicitHeight: pill.height
@@ -59,14 +135,17 @@ Item {
BarPill {
id: pill
visible: _pillVisible
opacity: _pillOpacity
screen: root.screen
oppositeDirection: BarService.getPillDirection(root)
icon: _dynamicIcon !== "" ? _dynamicIcon : customIcon
text: (!isVerticalBar || currentMaxTextLength > 0) ? _dynamicText : ""
icon: _pillIcon
text: _pillText
density: Settings.data.bar.density
rotateText: isVerticalBar && currentMaxTextLength > 0
autoHide: false
forceOpen: _dynamicText !== ""
forceOpen: _pillForceOpen
tooltipText: {
var tooltipLines = [];
@@ -18,6 +18,8 @@ ColumnLayout {
property bool valueParseJson: widgetData.parseJson !== undefined ? widgetData.parseJson : widgetMetadata.parseJson
property int valueMaxTextLengthHorizontal: widgetData?.maxTextLength?.horizontal ?? widgetMetadata?.maxTextLength?.horizontal
property int valueMaxTextLengthVertical: widgetData?.maxTextLength?.vertical ?? widgetMetadata?.maxTextLength?.vertical
property string valueHideMode: (widgetData.hideMode !== undefined) ? widgetData.hideMode : widgetMetadata.hideMode
property bool valueShowIcon: (widgetData.showIcon !== undefined) ? widgetData.showIcon : widgetMetadata.showIcon
function saveSettings() {
var settings = Object.assign({}, widgetData || {});
@@ -39,6 +41,8 @@ ColumnLayout {
settings.textCollapse = textCollapseInput.text;
settings.textStream = valueTextStream;
settings.parseJson = valueParseJson;
settings.showIcon = valueShowIcon;
settings.hideMode = valueHideMode;
settings.maxTextLength = {
"horizontal": valueMaxTextLengthHorizontal,
"vertical": valueMaxTextLengthVertical
@@ -88,6 +92,15 @@ ColumnLayout {
}
}
NToggle {
id: showIconToggle
label: I18n.tr("bar.widget-settings.custom-button.show-icon.label", "Show icon")
description: I18n.tr("bar.widget-settings.custom-button.show-icon.description", "Toggles the visibility of the widget's icon.")
checked: valueShowIcon
onToggled: checked => valueShowIcon = checked
visible: textCommandInput.text !== ""
}
RowLayout {
spacing: Style.marginM
@@ -328,6 +341,20 @@ ColumnLayout {
placeholderText: String(widgetMetadata.textIntervalMs || 3000)
text: widgetData && widgetData.textIntervalMs !== undefined ? String(widgetData.textIntervalMs) : ""
}
NComboBox {
id: hideModeComboBox
label: I18n.tr("bar.widget-settings.custom-button.hide-mode.label", "Hide mode")
description: I18n.tr("bar.widget-settings.custom-button.hide-mode.description", "Controls widget visibility when the command has no output.")
model: [
{ name: I18n.tr("bar.widget-settings.custom-button.hide-mode.alwaysExpanded", "Always expanded"), key: "alwaysExpanded" },
{ name: I18n.tr("bar.widget-settings.custom-button.hide-mode.expandWithOutput", "Expand when has output"), key: "expandWithOutput" },
{ name: I18n.tr("bar.widget-settings.custom-button.hide-mode.maxTransparent", "Max expanded but transparent"), key: "maxTransparent" }
]
currentKey: valueHideMode
onSelected: key => valueHideMode = key
visible: textCommandInput.text !== "" && valueTextStream == true
}
}
}
}
+2
View File
@@ -118,6 +118,8 @@ Singleton {
"CustomButton": {
"allowUserSettings": true,
"icon": "heart",
"showIcon": true,
"hideMode": "alwaysExpanded",
"leftClickExec": "",
"leftClickUpdateText": false,
"rightClickExec": "",