feat(qt): IconTextButton control and external-link icon

Shared button-with-icon control (used by the queue panel, Plex settings,
the log viewer and the library folder editor) plus the external-link
icon in every theme tint.

cc
This commit is contained in:
vicrodh committed 2026-08-10 17:05:56 -06:00
1 parent 36e8ec990d
commit b46924bcc0
10 files changed
+109

No files matched your search

+1
View File
@@ -103,6 +103,7 @@ fn main() {
"qml/controls/QualityBadge.qml",
"qml/controls/QualityMini.qml",
"qml/controls/SettingRow.qml",
"qml/controls/IconTextButton.qml",
"qml/controls/SettingsButton.qml",
"qml/controls/SettingsDivider.qml",
"qml/controls/SettingsSpacer.qml",
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#4285f4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M15 3h6v6" />
<path d="M10 14 21 3" />
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
</svg>

After

Width:  |  Height:  |  Size: 307 B

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M15 3h6v6" />
<path d="M10 14 21 3" />
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
</svg>

After

Width:  |  Height:  |  Size: 307 B

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ef4444" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M15 3h6v6" />
<path d="M10 14 21 3" />
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
</svg>

After

Width:  |  Height:  |  Size: 307 B

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#888888" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M15 3h6v6" />
<path d="M10 14 21 3" />
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
</svg>

After

Width:  |  Height:  |  Size: 307 B

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M15 3h6v6" />
<path d="M10 14 21 3" />
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
</svg>

After

Width:  |  Height:  |  Size: 307 B

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#cccccc" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M15 3h6v6" />
<path d="M10 14 21 3" />
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
</svg>

After

Width:  |  Height:  |  Size: 307 B

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#fbbf24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M15 3h6v6" />
<path d="M10 14 21 3" />
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
</svg>

After

Width:  |  Height:  |  Size: 307 B

@@ -0,0 +1,72 @@
// Small bordered icon+label button — the QML port of the private
// `IconTextButton` in crates/qbz-ui/ui/shell/LogViewerModal.slint:29-75.
//
// It exists because it AUTO-SIZES TO ITS LABEL. `controls/SettingsButton.qml`
// carries `minWidth: 160`, which is right for a settings row's right rail and
// wrong for a footer of five buttons — using it there overflowed the panel and
// the labels collided (observed 2026-08-05).
//
// Geometry is 1:1 with the reference: height 32, Radius.sm, 1px border-subtle,
// surface-elevated (surface-hover on hover), opacity 0.4 when disabled,
// 12px side padding, 7px gap, 14px icon, 13px medium label. `danger` recolors
// BOTH the icon and the label — it does not fill.
import QtQuick
import com.blitzfc.qbz
import "../theme"
Rectangle {
id: root
property string label: ""
property string iconName: ""
/// The reference's `has-icon`: an icon-less variant keeps the same
/// padding and height.
property bool hasIcon: true
property bool danger: false
property bool btnEnabled: true
signal clicked()
QbzTheme { id: theme }
height: 32
// Auto-size: padding + optional icon + gap + label.
width: 12 + (root.hasIcon ? 14 + 7 : 0) + labelText.implicitWidth + 12
radius: theme.radiusSm
border.width: 1
border.color: theme.borderSubtle
color: hoverArea.containsMouse && root.btnEnabled ? theme.surfaceHover : theme.surfaceElevated
opacity: root.btnEnabled ? 1.0 : 0.4
Row {
anchors.centerIn: parent
spacing: 7
QbzIcon {
visible: root.hasIcon
anchors.verticalCenter: parent.verticalCenter
name: root.iconName
width: 14
height: 14
// `favorite` is the port's documented stand-in for the danger red
// (icon_tint_qt.rs:63); there is no `danger` bake.
tintName: root.danger ? "favorite" : "secondary"
}
Text {
id: labelText
anchors.verticalCenter: parent.verticalCenter
text: root.label
color: root.danger ? theme.danger : theme.textSecondary
font.pixelSize: 13
font.weight: theme.weightMedium
}
}
MouseArea {
id: hoverArea
anchors.fill: parent
hoverEnabled: true
cursorShape: root.btnEnabled ? Qt.PointingHandCursor : Qt.ArrowCursor
onClicked: if (root.btnEnabled) root.clicked()
}
}
+1
View File
@@ -184,6 +184,7 @@ const MASTERS: &[(&str, &str)] = &[
("disc", include_str!("../qml/assets/icons/primary/disc.svg")),
("element-connect", include_str!("../qml/assets/icons/primary/element-connect.svg")),
("ellipsis", include_str!("../qml/assets/icons/primary/ellipsis.svg")),
("external-link", include_str!("../qml/assets/icons/primary/external-link.svg")),
("eye-off", include_str!("../qml/assets/icons/primary/eye-off.svg")),
("eye", include_str!("../qml/assets/icons/primary/eye.svg")),
("folder-open", include_str!("../qml/assets/icons/primary/folder-open.svg")),