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. `clonedFrom`, and the shell routes those calls here.
`BarWidget.qml` swaps the stock `\ue900` glyph from the `omarchy` icon font for `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` `branding/blob_icon.svg`. An SVG is text, so the fill is rewritten to the bar
cannot be recolored, so the icon is used as an alpha mask over a rectangle foreground in QML and the result handed to `Image` as a base64 `data:` URL -
filled with the bar foreground. That keeps it following the theme and the bar's the same trick the `theme-set` hook uses with `sed` for the fastfetch logo, so
own color animation the way a glyph did, and it means the black fill in the both consumers of the icon work the same way. The color is read off
source file never matters. `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 `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` 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
import QtQuick.Effects
import Quickshell import Quickshell
import Quickshell.Io
import qs.Commons import qs.Commons
import qs.Ui import qs.Ui
@@ -8,19 +8,43 @@ BarWidget {
id: root id: root
moduleName: "omarchy.menu" 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) 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 implicitWidth: button.implicitWidth
implicitHeight: button.implicitHeight implicitHeight: button.implicitHeight
FileView {
path: root.iconPath
watchChanges: true
printErrors: false
onLoaded: root.iconSvg = text()
onLoadFailed: root.iconSvg = ""
onFileChanged: reload()
}
WidgetButton { WidgetButton {
id: button id: button
anchors.fill: parent anchors.fill: parent
bar: root.bar bar: root.bar
labelVisible: false text: "\ue900"
hasVisualContent: true fontFamily: "omarchy"
fixedWidth: root.iconSize + Style.spaceReal(15) labelVisible: !root.iconReady
fixedWidth: root.iconReady ? root.iconSize + Style.spaceReal(15) : -1
horizontalMargin: 7.5 horizontalMargin: 7.5
onPressed: function(button) { onPressed: function(button) {
if (!root.bar) return if (!root.bar) return
@@ -29,30 +53,15 @@ BarWidget {
} }
Image { Image {
id: iconMask id: icon
source: root.iconPath
visible: false
smooth: true
sourceSize.width: root.iconSize * 2
sourceSize.height: root.iconSize * 2
}
Rectangle {
anchors.centerIn: parent anchors.centerIn: parent
width: root.iconSize width: root.iconSize
height: root.iconSize height: root.iconSize
color: button.foreground source: root.iconUrl
visible: iconMask.status === Image.Ready sourceSize.width: root.iconSize * 2
layer.enabled: true sourceSize.height: root.iconSize * 2
layer.effect: MultiEffect { smooth: true
maskEnabled: true visible: root.iconReady
maskSource: iconMask
}
Behavior on color {
enabled: !root.bar || root.bar.foregroundAnimationEnabled
ColorAnimation { duration: 160 }
}
} }
} }
} }