diff options
| -rw-r--r-- | README.md | 13 | ||||
| -rw-r--r-- | SPEC.md | 21 | ||||
| -rw-r--r-- | TODO-icons.md | 50 | ||||
| -rwxr-xr-x | bin/wb-icon | 57 | ||||
| -rwxr-xr-x | install.sh | 39 | ||||
| -rw-r--r-- | modules/extras/taskbar.jsonc | 6 | ||||
| -rw-r--r-- | styles/modules.css | 34 |
7 files changed, 177 insertions, 43 deletions
@@ -34,9 +34,10 @@ from there, and `install.sh` fails clearly if it is missing. ## No glyphs -Every symbol on this bar is a real icon from `Material-Black-Plum-Suru`, the -theme GTK already uses. Icons keep their own colours, matching every other -application on the desktop. +Every symbol on this bar is a real icon from the desktop's icon theme, read +from gsettings, so the bar follows a theme switch instead of naming one theme +forever. Icons keep their own colours, matching every other application on the +desktop. waybar offers three ways to draw one, and this bar uses all three because no single one covers everything: @@ -63,9 +64,9 @@ with nothing to say why, so it fails while someone is watching instead. ### Why the icons do not follow the accent -2466 of the 5169 panel icons in `Material-Black-Plum-Suru` carry a hardcoded -gradient and are not recolourable. That is intended here: icons match the rest -of the desktop rather than the bar. +2466 of the 5169 panel icons in `Material-Black-Plum-Suru`, the theme this +desktop runs, carry a hardcoded gradient and are not recolourable. That is +intended here: icons match the rest of the desktop rather than the bar. The icons this repo ships are the exception, because the icon theme has none of them. `icons/slackware.svg` (Simple Icons, CC0) is single-path monochrome, @@ -10,8 +10,9 @@ icons from the desktop's own icon theme rather than font glyphs. Three things at once: - **No font glyphs.** Every module that shows a symbol shows a real icon from - `Material-Black-Plum-Suru`, the theme GTK already uses. Icons keep their own - colours, because that is what every other application on this desktop shows. + the desktop's icon theme, which `bin/wb-icon` reads from gsettings so a theme + switch reaches the bar. Icons keep their own colours, because that is what + every other application on this desktop shows. - **Colours from udt.** Pill backgrounds, text and states resolve from `palette/roles-<scheme>.conf`, so switching scheme recolours this bar with everything else. @@ -80,7 +81,9 @@ sits before the text, which is what makes it match the rest. | 7 | `internet-chat` | chat glyph | | 8 | `input-gaming` | joypad: games and media players | -All eight resolve in `Material-Black-Plum-Suru` today. +All eight resolve in `Material-Black-Plum-Suru`, the theme this desktop runs. +`wb-icon --selftest` checks them against whatever theme is active, so a switch +to a theme missing one fails the install rather than drawing an empty pill. ## How icons reach the bar @@ -88,9 +91,11 @@ Three mechanisms, because waybar offers three and no single one covers everything. This is the part worth reading before changing anything. **1. Native `icon-theme` (taskbar, tray).** `wlr/taskbar` takes `icon-theme` -and `icon-size` outright. Today it asks for `Papirus`, **which is not -installed**, so GTK has been silently falling back. Pointing it at -`Material-Black-Plum-Suru` is a bug fix, not a preference. +and `icon-size` outright. It used to ask for `Papirus`, **which is not +installed**, so GTK silently fell back. It now carries an `@ICON_THEME@` +placeholder that `install.sh` fills from gsettings, which is why that one +module file is generated rather than symlinked. Being a config key and not +CSS, a change needs waybar restarted, not signalled. **2. The `image` module (volume, presentation mode).** `image` runs `exec` and expects `$path\n$tooltip` on stdout, so a small script maps state to an icon @@ -110,8 +115,8 @@ stylesheet, not in the config.** Changing a workspace icon means editing CSS. ### Why the icons cannot follow the accent -2466 of the 5169 panel icons in `Material-Black-Plum-Suru` carry a hardcoded -`oomox` gradient (`#a08fbf` to `#5a33a3`) and seven further named gradients +2466 of the 5169 panel icons in `Material-Black-Plum-Suru`, the theme this +desktop runs, carry a hardcoded `oomox` gradient (`#a08fbf` to `#5a33a3`) and seven further named gradients each. They are not monochrome and not recolourable by CSS `color`. So icons show their own colours and do not track the scheme, which is the intended behaviour here: they match every other application on the desktop. diff --git a/TODO-icons.md b/TODO-icons.md new file mode 100644 index 0000000..ac904f7 --- /dev/null +++ b/TODO-icons.md @@ -0,0 +1,50 @@ +# Unhardcode the icon theme + +Done. The active icon theme is now read from +`gsettings get org.gnome.desktop.interface icon-theme`, which is what the +appearance drawer's Icons tab sets, so a switch there reaches this bar instead +of being silently reverted. + +## How it works + +`bin/wb-icon` reads the gsettings key once and resolves names through GTK's +icon lookup. Nothing else may assume a path shape: themes disagree about both +the subdirectory and the file extension (the Suru themes keep `web-browser` at +`categories/22/*.svg`, elementary-xfce at `apps/22/*.png`), so substituting one +directory prefix would have broken on any non-Suru theme. + +Two new modes serve `install.sh`: + +- `wb-icon --path NAME` prints one absolute path +- `wb-icon --theme` prints the resolved theme name + +`styles/modules.css` names icons as `@ICON:name@` placeholders and +`modules/extras/taskbar.jsonc` carries `@ICON_THEME@`; `install.sh` resolves +both at install time. taskbar.jsonc is generated for this reason and is no +longer symlinked. + +## After a theme switch + +Re-run `install.sh`, then **restart waybar**. CSS reloads itself through +`reload_style_on_change`, but `taskbar.jsonc` is config: a signal is not enough. + +## Fixed elsewhere + +- `~/.config/rofi/config.rasi` — `icon-theme` commented out, so rofi follows + the desktop theme. +- `~/.local/share/applications/firefox-clean.desktop` — absolute `Icon=` path + replaced with the bare name `firefox-aurora`, which the desktop-entry spec + resolves through the active theme (and which picks a 48px icon rather than + the 16px one that was pinned). + +## Not a conflict + +`~/.config/hypr/hyprqt6engine.conf`'s `icon_theme` is commented out. +unified-desktop-theme's `templates/qt-gtk/*` are reference copies that nothing +installs; see that repo's TODO-icons.md. + +## Caveat + +A theme lacking one of the 20 names this bar asks for fails the install at the +`--selftest` step rather than rendering empty pills. elementary-xfce, for +example, has no `text-editor` or `internet-chat`. diff --git a/bin/wb-icon b/bin/wb-icon index 9e011b9..1d76369 100755 --- a/bin/wb-icon +++ b/bin/wb-icon @@ -10,6 +10,7 @@ # # wb-icon volume # reads wpctl, prints the matching icon # wb-icon mic +# wb-icon --path NAME # absolute path of one icon, for install.sh # wb-icon --selftest # every name this bar asks for must resolve # # Icons keep their own colours: this desktop's icon theme draws them with @@ -24,9 +25,29 @@ import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk # noqa: E402 (must follow require_version) -# The icon theme to draw from. Kept in one place because changing it is a -# single edit, and because --selftest needs the same value the modules use. -THEME = "Material-Black-Plum-Suru" +# The icon theme to draw from: whatever the desktop is set to. The appearance +# drawer switches it through gsettings, so reading that key is what keeps this +# bar following a switch instead of silently pointing at the old theme. +# +# The fallback only matters when gsettings is unreadable; GTK falls back to +# hicolor on its own if the named theme is missing. +FALLBACK_THEME = "Adwaita" + + +def icon_theme(): + """The desktop's icon theme name, per gsettings.""" + try: + out = subprocess.run( + ["gsettings", "get", "org.gnome.desktop.interface", "icon-theme"], + capture_output=True, text=True, timeout=2, + ).stdout.strip() + except (OSError, subprocess.SubprocessError): + return FALLBACK_THEME + # gsettings quotes its answer: 'Material-Black-Plum-Suru' + return out.strip("'\"") or FALLBACK_THEME + + +THEME = icon_theme() SIZE = 22 @@ -47,7 +68,13 @@ ICONS = { def lookup(name): - """Absolute path of an icon in THEME, or None.""" + """Absolute path of an icon in THEME, or None. + + GTK resolves the theme's own layout, so this works across icon themes + that disagree about subdirectory and file extension: the Suru themes keep + web-browser under categories/22/*.svg, elementary-xfce under apps/22/*.png. + Nothing outside this function may assume a path shape. + """ theme = Gtk.IconTheme.new() theme.set_custom_theme(THEME) info = theme.lookup_icon(name, SIZE, 0) @@ -138,7 +165,8 @@ def selftest(): def main(argv): if len(argv) < 2 or argv[1] in ("-h", "--help"): - print(__doc__ or "usage: wb-icon <volume|mic|idle|--selftest> [state]") + print(__doc__ or + "usage: wb-icon <volume|mic|--path NAME|--theme|--selftest>") return 0 what = argv[1] @@ -146,6 +174,25 @@ def main(argv): if what == "--selftest": return selftest() + # install.sh bakes icon paths into the generated stylesheet, because CSS + # cannot look an icon up by name. It asks here so that the theme is + # resolved in exactly one place. + if what == "--path": + if len(argv) < 3: + print("wb-icon: --path needs an icon name", file=sys.stderr) + return 2 + path = lookup(argv[2]) + if not path: + print(f"wb-icon: no icon named {argv[2]!r} in {THEME}", + file=sys.stderr) + return 1 + print(path) + return 0 + + if what == "--theme": + print(THEME) + return 0 + if what == "volume": pct, muted = wpctl("@DEFAULT_AUDIO_SINK@") if pct is None: @@ -15,7 +15,6 @@ set -euo pipefail repo="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" target="$HOME/.config/waybar-udt" udt="$(cd "$repo/../unified-desktop-theme" 2>/dev/null && pwd || true)" -icons="$HOME/.local/share/icons/Material-Black-Plum-Suru" mkdir -p "$target/modules/hyprland" "$target/modules/custom" \ "$target/modules/extras" "$target/modules/image" \ @@ -96,7 +95,7 @@ for f in modules/clock.jsonc \ modules/custom/vmdot.jsonc modules/custom/privacy_dots.jsonc \ modules/custom/presentation.jsonc modules/custom/mail.jsonc \ modules/custom/launcher.jsonc \ - modules/extras/taskbar.jsonc modules/extras/tray.jsonc \ + modules/extras/tray.jsonc \ modules/image/volume.jsonc modules/image/language.jsonc; do ln -sfn "$repo/$f" "$target/$f" done @@ -105,11 +104,37 @@ for f in style.css fonts.css global.css pills.css; do ln -sfn "$repo/styles/$f" "$target/styles/$f" done -# modules.css is generated rather than linked: it carries the icon theme's -# absolute path, which cannot be committed, and the accent-tinted launcher. -sed -e "s|@ICONS@|$icons|g" -e "s|@CONFIG@|$target|g" \ +# modules.css is generated rather than linked: it carries absolute icon paths, +# which cannot be committed, and the accent-tinted launcher. +# +# Each @ICON:name@ is resolved by wb-icon, which reads the desktop's icon theme +# from gsettings. Resolving per name rather than substituting one directory is +# what makes a theme switch work at all: themes disagree about both the +# subdirectory and the file extension (Suru keeps web-browser at +# categories/22/*.svg, elementary-xfce at apps/22/*.png), so only a real icon +# lookup knows the path. An unresolved name is left as-is and reported; the +# selftest below is what turns that into a failed install. +sed -e "s|@CONFIG@|$target|g" \ "$repo/styles/modules.css" > "$target/styles/modules.css" +for name in $(grep -o '@ICON:[a-z0-9-]*@' "$target/styles/modules.css" \ + | sed 's/@ICON:\(.*\)@/\1/' | sort -u); do + if path="$("$repo/bin/wb-icon" --path "$name")"; then + sed -i "s|@ICON:$name@|$path|g" "$target/styles/modules.css" + else + echo "warning: no icon named '$name' in the current icon theme" >&2 + fi +done + +# taskbar.jsonc names the icon theme, which is gsettings' to say, so it is +# generated for the same reason modules.css is. Every other module is a symlink. +# An earlier install symlinked this file into the repo. Writing through that +# link would overwrite the committed source with a generated copy, so the link +# is removed first; the redirect below then creates a real file. +rm -f "$target/modules/extras/taskbar.jsonc" +sed "s|@ICON_THEME@|$("$repo/bin/wb-icon" --theme)|g" \ + "$repo/modules/extras/taskbar.jsonc" > "$target/modules/extras/taskbar.jsonc" + # The launcher module carries no generated path any more: its mark is a CSS # background, so modules.css owns the paths and the module file is a plain # symlink like every other. @@ -149,7 +174,9 @@ ln -sfn "$repo/bin/wb-lang" "$HOME/bin/wb-lang" # ----------------------------------------------------------------- checks -- # A missing icon renders as an empty pill and nothing says why, so the names -# are checked here, while someone is watching. +# are checked here, while someone is watching. The theme is whatever gsettings +# says, so this also catches a switch to a theme that lacks one of these names. +echo "icon theme: $("$repo/bin/wb-icon" --theme)" "$repo/bin/wb-icon" --selftest echo diff --git a/modules/extras/taskbar.jsonc b/modules/extras/taskbar.jsonc index c7f88ea..b47448a 100644 --- a/modules/extras/taskbar.jsonc +++ b/modules/extras/taskbar.jsonc @@ -1,7 +1,9 @@ // Open applications, drawn with each application's own icon. // // icon-theme used to name Papirus, which is not installed on this machine, so -// GTK fell back silently. It now names the theme the rest of the desktop uses. +// GTK fell back silently. It is now @ICON_THEME@, substituted by install.sh +// with whatever gsettings reports, so a theme switch reaches this bar too. +// That is also why this file is generated rather than symlinked. // // all-outputs makes this a global window switcher rather than a list of what // is on this screen: with it false, the DP-3 bar showed only the two windows @@ -11,7 +13,7 @@ "wlr/taskbar": { "all-outputs": true, "format": "{icon}", - "icon-theme": "Material-Black-Plum-Suru", + "icon-theme": "@ICON_THEME@", "icon-size": 20, "tooltip": true, "tooltip-format": "{title}", diff --git a/styles/modules.css b/styles/modules.css index ee5c4b2..b6fd38d 100644 --- a/styles/modules.css +++ b/styles/modules.css @@ -2,8 +2,10 @@ * * The icons for the workspaces, the clock pair, the launcher and the language * live here rather than in the config, because waybar's format-icons takes - * text and not paths. @ICONS@ is substituted by install.sh with the icon - * theme's absolute path, so the theme can move without editing this file. + * text and not paths. Each icon placeholder below is substituted by install.sh + * with the absolute path `wb-icon --path <name>` resolves, so this file names + * icons and never a directory: themes disagree about both subdirectory and + * file extension, and only a real icon lookup knows which. */ /* ------------------------------------------------------------------ * @@ -59,11 +61,11 @@ } #clock.date { - background-image: url("@ICONS@/mimetypes/22/x-office-calendar.svg"); + background-image: url("@ICON:x-office-calendar@"); } #clock.time { - background-image: url("@ICONS@/categories/22/clock.svg"); + background-image: url("@ICON:clock@"); } /* ------------------------------------------------------------- * @@ -104,14 +106,14 @@ /* The eight. Generic names, so the bar does not lie when the application on a * workspace changes. */ -#workspaces button:nth-child(1) { background-image: url("@ICONS@/categories/22/web-browser.svg"); } -#workspaces button:nth-child(2) { background-image: url("@ICONS@/categories/22/text-editor.svg"); } -#workspaces button:nth-child(3) { background-image: url("@ICONS@/categories/22/utilities-terminal.svg"); } -#workspaces button:nth-child(4) { background-image: url("@ICONS@/devices/22/network-server.svg"); } -#workspaces button:nth-child(5) { background-image: url("@ICONS@/actions/22/document-edit.svg"); } -#workspaces button:nth-child(6) { background-image: url("@ICONS@/categories/22/applications-graphics.svg"); } -#workspaces button:nth-child(7) { background-image: url("@ICONS@/actions/22/dialog-messages.svg"); } -#workspaces button:nth-child(8) { background-image: url("@ICONS@/devices/22/input-gaming.svg"); } +#workspaces button:nth-child(1) { background-image: url("@ICON:web-browser@"); } +#workspaces button:nth-child(2) { background-image: url("@ICON:text-editor@"); } +#workspaces button:nth-child(3) { background-image: url("@ICON:utilities-terminal@"); } +#workspaces button:nth-child(4) { background-image: url("@ICON:network-server@"); } +#workspaces button:nth-child(5) { background-image: url("@ICON:document-edit@"); } +#workspaces button:nth-child(6) { background-image: url("@ICON:applications-graphics@"); } +#workspaces button:nth-child(7) { background-image: url("@ICON:dialog-messages@"); } +#workspaces button:nth-child(8) { background-image: url("@ICON:input-gaming@"); } /* ------------------------------------------------ * * Language: a flag, shipped by this repo. * @@ -135,7 +137,7 @@ * ------------------------------------------------------ */ #custom-mail { - background-image: url("@ICONS@/actions/22/mail-unread.svg"); + background-image: url("@ICON:mail-unread@"); background-repeat: no-repeat; background-position: 10px center; background-size: 16px 16px; @@ -240,13 +242,13 @@ } #custom-presentation.deactivated { - background-image: url("@ICONS@/categories/22/preferences-desktop-screensaver.svg"); + background-image: url("@ICON:preferences-desktop-screensaver@"); } #custom-presentation.activated { - background-image: url("@ICONS@/mimetypes/22/x-office-presentation.svg"); + background-image: url("@ICON:x-office-presentation@"); } #custom-presentation.down { - background-image: url("@ICONS@/status/22/dialog-warning.svg"); + background-image: url("@ICON:dialog-warning@"); } |
