Properly render tray icons

This commit is contained in:
David Heinemeier Hansson
2026-07-13 09:56:20 -07:00
parent 17efd5e493
commit cb8ab2d9f4
2 changed files with 46 additions and 13 deletions
+1
View File
@@ -12,6 +12,7 @@ hl.env("HYPRCURSOR_SIZE", "24")
hl.env("GDK_BACKEND", "wayland,x11,*")
hl.env("QT_QPA_PLATFORM", "wayland;xcb")
hl.env("QT_STYLE_OVERRIDE", "kvantum")
hl.env("QT_QPA_PLATFORMTHEME", "gtk3")
hl.env("MOZ_ENABLE_WAYLAND", "1")
hl.env("ELECTRON_OZONE_PLATFORM_HINT", "wayland")
hl.env("OZONE_PLATFORM", "wayland")
+45 -13
View File
@@ -1,5 +1,6 @@
import Quickshell
import QtQuick
import QtQuick.Effects
import Quickshell.Services.SystemTray
import Quickshell.Widgets
import qs.Commons
@@ -50,14 +51,20 @@ BarWidget {
}
function trayIconSource(icon) {
var value = String(icon || "")
var marker = "?path="
var markerIndex = value.indexOf(marker)
if (markerIndex === -1) return value
// Quickshell already resolves the tray icon into a ready-to-use image://
// URL, including a "?path=" fallback search dir for apps that ship their
// tray icon outside a standard theme (e.g. Steam's flat public/ dir). Hand
// it straight to IconImage; guessing a theme sub-directory here only broke
// apps whose layout didn't match the guess.
return String(icon || "")
}
var name = value.substring(0, markerIndex).split("/").pop()
var iconPath = value.substring(markerIndex + marker.length).split("&")[0]
return Util.fileUrl(iconPath + "/hicolor/16x16/status/" + name + ".png")
// Symbolic icons ship a fixed fill (often near-white) that the host is meant
// to recolor to its foreground; detect them by the freedesktop "-symbolic"
// name suffix so they can be tinted instead of rendered as-is.
function iconIsSymbolic(icon) {
var name = String(icon || "").split("?")[0]
return name.slice(-9) === "-symbolic"
}
function trayTooltip(item) {
@@ -373,14 +380,13 @@ BarWidget {
readonly property bool isPinned: root.pinnedIds.indexOf(itemId) !== -1
readonly property bool isHidden: root.hiddenIds.indexOf(itemId) !== -1
IconImage {
TrayIcon {
id: rowIcon
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
implicitSize: 16
width: 16
height: 16
source: root.trayIconSource(rowRoot.modelData.icon)
icon: rowRoot.modelData.icon
}
Text {
@@ -559,6 +565,33 @@ BarWidget {
}
}
// Renders a tray icon, recoloring symbolic icons to the bar foreground so
// they stay visible on any theme (a raw symbolic icon keeps its baked-in
// fill and disappears against a matching background).
component TrayIcon: Item {
id: trayIconRoot
required property var icon
readonly property bool symbolic: root.iconIsSymbolic(icon)
IconImage {
id: trayIconImage
anchors.fill: parent
implicitSize: Math.round(Math.min(parent.width, parent.height))
source: root.trayIconSource(trayIconRoot.icon)
// Kept as a hidden layer so the effect can sample it as a texture.
visible: !trayIconRoot.symbolic
layer.enabled: trayIconRoot.symbolic
}
MultiEffect {
anchors.fill: trayIconImage
source: trayIconImage
visible: trayIconRoot.symbolic
colorization: 1.0
colorizationColor: root.foreground
}
}
component TrayItem: Item {
id: trayItemRoot
@@ -572,12 +605,11 @@ BarWidget {
root.openTrayMenu(trayItemRoot.modelData, trayItemRoot, mouse)
}
IconImage {
TrayIcon {
anchors.centerIn: parent
implicitSize: Style.space(12)
width: Style.space(12)
height: Style.space(12)
source: root.trayIconSource(trayItemRoot.modelData.icon)
icon: trayItemRoot.modelData.icon
}
MouseArea {