aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--deckit/server.py15
-rw-r--r--static/modules/windows.js5
-rw-r--r--tests/test_server.py10
3 files changed, 24 insertions, 6 deletions
diff --git a/deckit/server.py b/deckit/server.py
index e392041..6bea422 100644
--- a/deckit/server.py
+++ b/deckit/server.py
@@ -84,12 +84,15 @@ def icon_path(name: str, theme: str | None = None, bases=ICON_BASES):
"""An app icon from the installed icon theme, so launchers follow the theme."""
if not ICON.fullmatch(name):
return None
- theme = theme or icon_theme()
- for base in bases:
- for sub in ("apps/scalable", "apps/64", "apps/48", "apps/32"):
- path = Path(base) / theme / sub / f"{name}.svg"
- if path.is_file():
- return path
+ # hicolor last: the spec's fallback every theme inherits, where apps
+ # install their own icon when the theme has none for them.
+ for th in (theme or icon_theme(), "hicolor"):
+ for base in bases:
+ # Themes lay out category/size (candy-icons) or size/category (hicolor).
+ for sub in ("apps/scalable", "apps/64", "apps/48", "apps/32", "scalable/apps"):
+ path = Path(base) / th / sub / f"{name}.svg"
+ if path.is_file():
+ return path
return None
diff --git a/static/modules/windows.js b/static/modules/windows.js
index d3e882e..7404f03 100644
--- a/static/modules/windows.js
+++ b/static/modules/windows.js
@@ -110,6 +110,11 @@ export default {
},
update(st, el) {
+ // Polled every second: rebuilding unchanged rows reloads every icon
+ // through its fallbacks, which flickers, so only a change re-renders.
+ const key = JSON.stringify(st);
+ if (key === el._last) return;
+ el._last = key;
const ws = el.querySelector(".ws");
ws.textContent = "";
for (const id of st.workspaces ?? []) {
diff --git a/tests/test_server.py b/tests/test_server.py
index a8fdf0d..7f2ee1c 100644
--- a/tests/test_server.py
+++ b/tests/test_server.py
@@ -128,6 +128,16 @@ class Server(unittest.TestCase):
self.assertIsNone(srv.icon_path("../x", "candy-icons", [base]))
self.assertIsNone(srv.icon_path("nope", "candy-icons", [base]))
+ def test_icon_path_falls_back_to_hicolor(self):
+ import tempfile
+ from pathlib import Path
+ base = Path(tempfile.mkdtemp())
+ (base / "candy-icons" / "apps" / "scalable").mkdir(parents=True)
+ d = base / "hicolor" / "scalable" / "apps"
+ d.mkdir(parents=True)
+ (d / "pcmanfm-qt.svg").write_text("<svg/>")
+ self.assertEqual(srv.icon_path("pcmanfm-qt", "candy-icons", [base]), d / "pcmanfm-qt.svg")
+
def test_art_route(self):
from unittest import mock
with mock.patch.object(srv, "art", return_value=(b"PNGDATA", "image/png")):