Tint the bar icon SVG directly instead of masking it

This commit is contained in:
2026-08-21 15:29:33 -04:00
parent a7364eb642
commit 03eb10d3db
2 changed files with 43 additions and 31 deletions
+8 -5
View File
@@ -87,11 +87,14 @@ and the root menu (Super + Alt + Space) are both wider. The two oversized menus
`clonedFrom`, and the shell routes those calls here.
`BarWidget.qml` swaps the stock `\ue900` glyph from the `omarchy` icon font for
`branding/blob_icon.svg`. Qt renders SVG through `qt6-svg`, but an `Image`
cannot be recolored, so the icon is used as an alpha mask over a rectangle
filled with the bar foreground. That keeps it following the theme and the bar's
own color animation the way a glyph did, and it means the black fill in the
source file never matters.
`branding/blob_icon.svg`. An SVG is text, so the fill is rewritten to the bar
foreground in QML and the result handed to `Image` as a base64 `data:` URL -
the same trick the `theme-set` hook uses with `sed` for the fastfetch logo, so
both consumers of the icon work the same way. The color is read off
`button.foreground` rather than the theme, which keeps the icon on the bar's own
foreground animation, and rebuilding the URL is what reloads the image, so there
is no image cache to bust. The glyph stays as the `text` fallback and is shown
whenever the SVG cannot be read or decoded, so the button is never blank.
`mergeMenuSources` in `MenuModel.js` reads the default menu first and the user
extension second, so a row that only exists in `extensions/omarchy-menu.jsonc`
+35 -26
View File
@@ -1,6 +1,6 @@
import QtQuick
import QtQuick.Effects
import Quickshell
import Quickshell.Io
import qs.Commons
import qs.Ui
@@ -8,19 +8,43 @@ BarWidget {
id: root
moduleName: "omarchy.menu"
readonly property string iconPath: "file://" + Quickshell.env("HOME") + "/.config/omarchy/branding/blob_icon.svg"
readonly property string iconPath: Quickshell.env("HOME") + "/.config/omarchy/branding/blob_icon.svg"
readonly property int iconSize: Math.round(Style.font.body * 1.35)
property string iconSvg: ""
readonly property string iconColor: hexColor(button.foreground)
readonly property string tintedSvg: iconSvg.replace(/fill="#000000"/g, 'fill="' + iconColor + '"')
readonly property string iconUrl: iconSvg.length > 0 ? "data:image/svg+xml;base64," + Qt.btoa(tintedSvg) : ""
readonly property bool iconReady: icon.status === Image.Ready
function hexColor(value) {
function channel(fraction) {
return ("0" + Math.round(fraction * 255).toString(16)).slice(-2)
}
return "#" + channel(value.r) + channel(value.g) + channel(value.b)
}
implicitWidth: button.implicitWidth
implicitHeight: button.implicitHeight
FileView {
path: root.iconPath
watchChanges: true
printErrors: false
onLoaded: root.iconSvg = text()
onLoadFailed: root.iconSvg = ""
onFileChanged: reload()
}
WidgetButton {
id: button
anchors.fill: parent
bar: root.bar
labelVisible: false
hasVisualContent: true
fixedWidth: root.iconSize + Style.spaceReal(15)
text: "\ue900"
fontFamily: "omarchy"
labelVisible: !root.iconReady
fixedWidth: root.iconReady ? root.iconSize + Style.spaceReal(15) : -1
horizontalMargin: 7.5
onPressed: function(button) {
if (!root.bar) return
@@ -29,30 +53,15 @@ BarWidget {
}
Image {
id: iconMask
source: root.iconPath
visible: false
smooth: true
sourceSize.width: root.iconSize * 2
sourceSize.height: root.iconSize * 2
}
Rectangle {
id: icon
anchors.centerIn: parent
width: root.iconSize
height: root.iconSize
color: button.foreground
visible: iconMask.status === Image.Ready
layer.enabled: true
layer.effect: MultiEffect {
maskEnabled: true
maskSource: iconMask
}
Behavior on color {
enabled: !root.bar || root.bar.foregroundAnimationEnabled
ColorAnimation { duration: 160 }
}
source: root.iconUrl
sourceSize.width: root.iconSize * 2
sourceSize.height: root.iconSize * 2
smooth: true
visible: root.iconReady
}
}
}