diff --git a/lib/solaar/ui/perkey/editor.py b/lib/solaar/ui/perkey/editor.py
index 9b0bab47..71a2e7c1 100644
--- a/lib/solaar/ui/perkey/editor.py
+++ b/lib/solaar/ui/perkey/editor.py
@@ -25,10 +25,13 @@ from __future__ import annotations
import logging
from enum import Enum
+from pathlib import Path
import gi
gi.require_version("Gtk", "3.0")
+from gi.repository import GdkPixbuf # NOQA: E402
+from gi.repository import Gio # NOQA: E402
from gi.repository import Gtk # NOQA: E402
from solaar.i18n import _ # NOQA: E402
@@ -57,6 +60,64 @@ _TOOL_LABELS = {
_TOOL_TOOLTIPS = {
"gradient": _("Drag to fade from previous color to active color"),
}
+_TOOL_ICON_NAMES = {
+ "single": "solaar-tool-brush-symbolic",
+ "rect": "solaar-tool-rect-symbolic",
+ "bucket": "solaar-tool-bucket-symbolic",
+}
+_TOOL_ICON_PIXEL_SIZE = 22
+
+_icon_search_path_added = False
+
+
+def _ensure_tool_icon_path() -> None:
+ """Register share/solaar/icons with the default GtkIconTheme so our
+ custom symbolic tool icons resolve by name. Idempotent."""
+ global _icon_search_path_added
+ if _icon_search_path_added:
+ return
+ theme = Gtk.IconTheme.get_default()
+ existing = set(theme.get_search_path() or [])
+ # Source-tree path: lib/solaar/ui/perkey/editor.py -> parents[4] = repo root
+ candidates = [
+ Path(__file__).resolve().parents[4] / "share" / "solaar" / "icons",
+ ]
+ for c in candidates:
+ if c.is_dir() and str(c) not in existing:
+ theme.append_search_path(str(c))
+ _icon_search_path_added = True
+
+
+def _tool_icon_image(icon_name: str, style_widget: Gtk.Widget) -> Gtk.Image | None:
+ """Load a Solaar tool icon and recolor it to match the given widget's
+ text foreground color, so the icons follow the active GTK theme
+ (light / dark / custom). Returns None if the icon can't be loaded.
+
+ GTK's stock symbolic loader (`load_symbolic_for_context`) only recolors
+ specific palette stand-ins (e.g. fill="#bebebe"); it ignores
+ `stroke="currentColor"`. We bypass it and substitute currentColor
+ ourselves so any SVG using the currentColor convention works.
+ """
+ _ensure_tool_icon_path()
+ theme = Gtk.IconTheme.get_default()
+ icon_info = theme.lookup_icon(icon_name, _TOOL_ICON_PIXEL_SIZE, Gtk.IconLookupFlags.FORCE_SIZE)
+ if icon_info is None:
+ return None
+ path = icon_info.get_filename()
+ if not path:
+ return None
+ fg = style_widget.get_style_context().get_color(Gtk.StateFlags.NORMAL)
+ color = f"#{int(fg.red * 255):02x}{int(fg.green * 255):02x}{int(fg.blue * 255):02x}"
+ try:
+ with open(path, "r", encoding="utf-8") as f:
+ svg = f.read()
+ svg = svg.replace("currentColor", color)
+ stream = Gio.MemoryInputStream.new_from_data(svg.encode("utf-8"))
+ pixbuf = GdkPixbuf.Pixbuf.new_from_stream_at_scale(stream, _TOOL_ICON_PIXEL_SIZE, _TOOL_ICON_PIXEL_SIZE, True)
+ return Gtk.Image.new_from_pixbuf(pixbuf)
+ except Exception as e:
+ logger.debug("recolor failed for %s: %s", icon_name, e)
+ return None
class PerKeyEditor(Gtk.Box):
@@ -81,9 +142,17 @@ class PerKeyEditor(Gtk.Box):
btn.set_tooltip_text(_TOOL_TOOLTIPS["gradient"])
else:
label, tip = _TOOL_LABELS.get(name, (name, ""))
- btn = Gtk.RadioButton.new_with_label_from_widget(first, label)
+ icon_name = _TOOL_ICON_NAMES.get(name)
+ btn = Gtk.RadioButton.new_from_widget(first)
btn.set_mode(False) # render as toggle button rather than radio
- btn.set_tooltip_text(tip)
+ image = _tool_icon_image(icon_name, btn) if icon_name else None
+ if image is not None:
+ btn.add(image)
+ btn.set_tooltip_text(tip or label)
+ btn.get_accessible().set_name(label)
+ else:
+ btn.set_label(label)
+ btn.set_tooltip_text(tip)
btn.connect(GtkSignal.TOGGLED.value, self._on_tool_toggled, name)
if first is None:
first = btn
diff --git a/share/solaar/icons/THIRD_PARTY.md b/share/solaar/icons/THIRD_PARTY.md
new file mode 100644
index 00000000..9326f212
--- /dev/null
+++ b/share/solaar/icons/THIRD_PARTY.md
@@ -0,0 +1,41 @@
+# Third-party assets in this directory
+
+Solaar itself is licensed under GPL-2.0 (see `LICENSE.txt` at the repository
+root). Some icons in this directory are sourced from third-party projects
+under permissive licenses compatible with GPL-2.0. Their original license
+notices are preserved as comments inside each SVG file.
+
+## Tabler Icons
+
+The following SVGs are derived from [Tabler Icons](https://tabler.io/icons),
+copyright (c) 2020-2024 Paweł Kuna, distributed under the MIT License:
+
+- `solaar-tool-brush-symbolic.svg` — from `brush`
+- `solaar-tool-rect-symbolic.svg` — from `paint`
+- `solaar-tool-bucket-symbolic.svg` — from `bucket-droplet`
+
+### MIT License (Tabler Icons)
+
+```
+MIT License
+
+Copyright (c) 2020-2024 Paweł Kuna
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+```
diff --git a/share/solaar/icons/solaar-tool-brush-symbolic.svg b/share/solaar/icons/solaar-tool-brush-symbolic.svg
new file mode 100644
index 00000000..e37803fc
--- /dev/null
+++ b/share/solaar/icons/solaar-tool-brush-symbolic.svg
@@ -0,0 +1,22 @@
+
+
diff --git a/share/solaar/icons/solaar-tool-bucket-symbolic.svg b/share/solaar/icons/solaar-tool-bucket-symbolic.svg
new file mode 100644
index 00000000..e73c5a83
--- /dev/null
+++ b/share/solaar/icons/solaar-tool-bucket-symbolic.svg
@@ -0,0 +1,21 @@
+
+
diff --git a/share/solaar/icons/solaar-tool-rect-symbolic.svg b/share/solaar/icons/solaar-tool-rect-symbolic.svg
new file mode 100644
index 00000000..844ca447
--- /dev/null
+++ b/share/solaar/icons/solaar-tool-rect-symbolic.svg
@@ -0,0 +1,21 @@
+
+