diff options
| author | Danilo M. <danix@danix.xyz> | 2026-09-17 14:37:03 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-09-17 14:37:03 +0200 |
| commit | 63fd47668a66ff546e330d22b64b7ce6e184a404 (patch) | |
| tree | 6dcf012333c020cfe8fd1058e8901b2ac3f46d4f /bin/udt-accent | |
| parent | d046bfa70d4276aecc2e80eea1924a7c0a5dc3ce (diff) | |
| download | unified-desktop-theme-63fd47668a66ff546e330d22b64b7ce6e184a404.tar.gz unified-desktop-theme-63fd47668a66ff546e330d22b64b7ce6e184a404.zip | |
fix: rank udt-accent power icons by name order and size
find_icon merged all candidate names into one set and ranked across
them, so a later alternate name could beat the first name present, and
size directories spelled 48x48 or scalable were not parsed. Try the
names in order instead: the first with a match wins, and within a name
prefer -symbolic, then scalable, then the largest size directory.
Update the docstring and selftest comment to match, extend the selftest
with size and name-order cases.
Add the missing GPL-2.0-only SPDX line.
Diffstat (limited to 'bin/udt-accent')
| -rwxr-xr-x | bin/udt-accent | 78 |
1 files changed, 59 insertions, 19 deletions
diff --git a/bin/udt-accent b/bin/udt-accent index 53839b1..a862ae9 100755 --- a/bin/udt-accent +++ b/bin/udt-accent @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only # udt-accent: pick a Catppuccin Macchiato accent matching a wallpaper. # Copyright (C) 2026 Danilo M. <danix@danix.xyz> # Licensed under the GNU General Public License v2 only. @@ -338,31 +339,42 @@ def icon_theme(): def find_icon(theme, names, dirs=None): - """Absolute path to the first of `names` found in `theme`, else "". - - A theme may ship an icon flat or as a symbolic variant at several sizes. - Prefer the symbolic variant, then the largest size directory, so the panel - gets a tintable silhouette. `dirs` is injectable for the selftest. + """Absolute path to the first of `names` present in `theme`, else "". + + The names are tried in order: the first that has any match wins. For that + name a theme may ship the icon flat or as a symbolic variant at several + sizes. Prefer the symbolic variant, then the largest size directory, where + `16x16`, `24x24`, `48x48`, a plain `16`/`48` and `scalable` are all parsed + and `scalable` counts as larger than any pixel size. `dirs` is injectable + for the selftest. """ if not theme: return "" dirs = ICON_DIRS if dirs is None else dirs - wanted = set(names) | {f"{name}-symbolic" for name in names} - matches = [] - for base in dirs: - root = base / theme - if not root.is_dir(): - continue - matches = [p for p in root.rglob("*.svg") if p.stem in wanted] - if matches: - break def rank(path): symbolic = path.stem.endswith("-symbolic") - sizes = [int(part) for part in path.parts if part.isdigit()] - return (symbolic, max(sizes, default=0)) - - return str(max(matches, key=rank)) if matches else "" + scalable = False + size = 0 + for part in path.parts: + if part == "scalable": + scalable = True + continue + match = re.fullmatch(r"(\d+)(?:x\d+)?", part) + if match: + size = max(size, int(match.group(1))) + return (symbolic, scalable, size) + + for name in names: + stems = {name, f"{name}-symbolic"} + for base in dirs: + root = base / theme + if not root.is_dir(): + continue + matches = [p for p in root.rglob("*.svg") if p.stem in stems] + if matches: + return str(max(matches, key=rank)) + return "" def power_icons(): @@ -516,7 +528,7 @@ def selftest(): # Power icons: an unknown or empty theme resolves to empty, so the greeter # falls back to its bundled SVGs; a real theme prefers the symbolic variant - # and a name list is honoured in order. + # at the largest size, and a name list is tried in order. assert find_icon("", ["system-shutdown"]) == "" assert find_icon("no-such-theme-udt", ["system-shutdown"]) == "" with tempfile.TemporaryDirectory() as tmpdir: @@ -534,6 +546,34 @@ def selftest(): assert find_icon("probe", ["system-hibernate", "system-suspend-hibernate"], dirs=[root]) == "" + # Among the sizes one name ships at, the largest directory wins. Both the + # WxH and plain-number spellings parse, and scalable is treated as largest. + with tempfile.TemporaryDirectory() as tmpdir: + root = Path(tmpdir) / "icons" + actions = root / "probe" / "actions" + for sub in ("16x16", "48x48"): + (actions / sub).mkdir(parents=True) + (actions / sub / "system-shutdown.svg").write_text("<svg/>") + got = find_icon("probe", ["system-shutdown"], dirs=[root]) + assert got.endswith("actions/48x48/system-shutdown.svg"), got + (actions / "scalable").mkdir() + (actions / "scalable" / "system-shutdown.svg").write_text("<svg/>") + got = find_icon("probe", ["system-shutdown"], dirs=[root]) + assert got.endswith("actions/scalable/system-shutdown.svg"), got + + # The first name in the list that exists wins, even when a later name also + # has a match. + with tempfile.TemporaryDirectory() as tmpdir: + root = Path(tmpdir) / "icons" + base = root / "probe" / "actions" / "48" + base.mkdir(parents=True) + (base / "system-shutdown.svg").write_text("<svg/>") + (base / "system-reboot.svg").write_text("<svg/>") + got = find_icon("probe", ["system-shutdown", "system-reboot"], dirs=[root]) + assert got.endswith("actions/48/system-shutdown.svg"), got + got = find_icon("probe", ["system-reboot", "system-shutdown"], dirs=[root]) + assert got.endswith("actions/48/system-reboot.svg"), got + with tempfile.TemporaryDirectory() as tmpdir: probe_image = Path(tmpdir) / "wall.png" probe_image.write_bytes(b"") |
