quickshell: tint YAMIS app icons to the wallpaper palette

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017EHJfY5Q72punsWX38nY1w
This commit is contained in:
rope 2026-08-21 14:40:02 +01:00
parent cb7b576a71
commit 1bea54f30c
2 changed files with 81 additions and 23 deletions

View file

@ -18,6 +18,7 @@ let
wallsJson = builtins.toJSON config.fred.theming.walls;
defaultWall = config.fred.theming.defaultWall;
themeApply = config.fred.theming.apply;
monoIcons = config.fred.theming.monoIcons;
in
{
config = lib.mkIf (lib.elem config.networking.hostName [ "FredOS-Gaming" "FredOS-Macbook" ]) {
@ -1312,6 +1313,10 @@ in
readonly property string wxFetch: "${wxFetchScript}"
readonly property string xdgOpen: "${pkgs.xdg-utils}/bin/xdg-open"
readonly property string themeApply: "${themeApply}"
// Prefix of the monochrome app-icon tree. AppIcon tests a
// resolved icon path against this to decide whether it may
// be tinted see the component for why that is not blanket.
readonly property string monoIconDir: "${monoIcons}"
}
'';
};
@ -1730,6 +1735,53 @@ in
// act on. Stroke weight is fixed by the design.
}
// AppIcon: an application icon in a square slot, tinted to
// the wallpaper's text colour when and only when it came
// from the monochrome set.
//
// YAMIS declares FollowsColorScheme=true, which is a KDE key:
// only Plasma rewrites the <style> block inside each SVG, so
// here every icon would otherwise render at the fixed #d8dee9
// baked into the file and sit dead against a themed bar.
// Overlaying at draw time follows the palette live and costs
// no disk copy (the same hidden-Image trick as the tray).
//
// The test is deliberately narrow rather than tinting every
// icon. A few apps have no YAMIS icon and fall through to
// Papirus, whose artwork encodes its detail as colour
// separation Spotify's waves are white-on-green, not cut
// out of the shape. Flatten one of those and you get a
// featureless blob, so full-colour fallbacks are left alone.
component AppIcon: Item {
id: _ai
property string path: ""
property int size: 32
readonly property bool mono:
_ai.path !== "" && _ai.path.indexOf(Commands.monoIconDir) !== -1
width: size
height: size
Image {
id: _aiImg
anchors.fill: parent
source: _ai.path
sourceSize.width: _ai.size
sourceSize.height: _ai.size
smooth: true
mipmap: true
// Hidden only when the overlay is drawing in its
// place; ColorOverlay reads it as a texture either way.
visible: _ai.path !== "" && !_ai.mono
}
ColorOverlay {
anchors.fill: _aiImg
source: _aiImg
visible: _ai.mono
color: Theme.base05
}
}
// BarSep: extra whitespace between widget groups. Groups are
// separated by air, not by lines a drawn rule read as clutter
// in a 46px column.
@ -1981,14 +2033,13 @@ in
Row {
width: parent.width
spacing: 4
visible: _nc.notif && (_nc.notif.appName !== "" || _appIcon.source != "")
Image {
visible: _nc.notif && (_nc.notif.appName !== "" || _appIcon.path != "")
AppIcon {
id: _appIcon
width: 14; height: 14
sourceSize.width: 14; sourceSize.height: 14
size: 14
anchors.verticalCenter: parent.verticalCenter
source: _nc.notif && _nc.notif.appIcon ? Quickshell.iconPath(_nc.notif.appIcon, true) : ""
visible: source != ""
path: _nc.notif && _nc.notif.appIcon ? Quickshell.iconPath(_nc.notif.appIcon, true) : ""
visible: path != ""
}
SText {
anchors.verticalCenter: parent.verticalCenter
@ -3320,14 +3371,10 @@ in
anchors.centerIn: parent
spacing: 4
Image {
AppIcon {
anchors.horizontalCenter: parent.horizontalCenter
visible: source != ""
width: 28
height: 28
sourceSize.width: 28
sourceSize.height: 28
source: pinCell.icon
path: pinCell.icon
size: 28
}
SText {
width: pinCell.width - 12
@ -3490,12 +3537,10 @@ in
width: 32
height: 32
Image {
AppIcon {
anchors.fill: parent
visible: source != ""
sourceSize.width: 32
sourceSize.height: 32
source: icon
path: icon
size: 32
}
SIcon {
anchors.centerIn: parent