PerKeyEditor: replace tool button labels with icons
The Brush / Rect / Fill tool buttons in the per-key color editor now show icons instead of text labels, with the original labels retained as tooltips and accessible names. Icons are recolored at load time to match the active GTK theme's text foreground (light grey on dark themes, dark on light), by reading the button's style context and substituting currentColor in the SVG before loading the pixbuf. GTK's stock symbolic loader is bypassed because it only recolors fill palette stand-ins and ignores stroke="currentColor", which is what the source SVGs use. Adds MIT-licensed Tabler icons under share/solaar/icons/, see THIRD_PARTY.md.
This commit is contained in:
parent
9b627410b6
commit
cf9a88bcaf
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
```
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<!--
|
||||
SPDX-License-Identifier: MIT
|
||||
SPDX-FileCopyrightText: 2020-2024 Paweł Kuna
|
||||
Source: Tabler Icons (https://tabler.io/icons) - "brush"
|
||||
License: MIT (https://github.com/tabler/tabler-icons/blob/main/LICENSE)
|
||||
-->
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M3 21v-4a4 4 0 1 1 4 4h-4" />
|
||||
<path d="M21 3a16 16 0 0 0 -12.8 10.2" />
|
||||
<path d="M21 3a16 16 0 0 1 -10.2 12.8" />
|
||||
<path d="M10.6 9a9 9 0 0 1 4.4 4.4" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 591 B |
|
|
@ -0,0 +1,21 @@
|
|||
<!--
|
||||
SPDX-License-Identifier: MIT
|
||||
SPDX-FileCopyrightText: 2020-2024 Paweł Kuna
|
||||
Source: Tabler Icons (https://tabler.io/icons) - "bucket-droplet"
|
||||
License: MIT (https://github.com/tabler/tabler-icons/blob/main/LICENSE)
|
||||
-->
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M5 16l1.465 1.638a2 2 0 1 1 -3.015 .099l1.55 -1.737" />
|
||||
<path d="M13.737 9.737c2.299 -2.3 3.23 -5.095 2.081 -6.245c-1.15 -1.15 -3.945 -.217 -6.244 2.082c-2.3 2.299 -3.231 5.095 -2.082 6.244c1.15 1.15 3.946 .218 6.245 -2.081" />
|
||||
<path d="M7.492 11.818c.362 .362 .768 .676 1.208 .934l6.895 4.047c1.078 .557 2.255 -.075 3.692 -1.512c1.437 -1.437 2.07 -2.614 1.512 -3.692c-.372 -.718 -1.72 -3.017 -4.047 -6.895a6.015 6.015 0 0 0 -.934 -1.208" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 887 B |
|
|
@ -0,0 +1,21 @@
|
|||
<!--
|
||||
SPDX-License-Identifier: MIT
|
||||
SPDX-FileCopyrightText: 2020-2024 Paweł Kuna
|
||||
Source: Tabler Icons (https://tabler.io/icons) - "paint"
|
||||
License: MIT (https://github.com/tabler/tabler-icons/blob/main/LICENSE)
|
||||
-->
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M5 5a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v2a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2l0 -2" />
|
||||
<path d="M19 6h1a2 2 0 0 1 2 2a5 5 0 0 1 -5 5l-5 0v2" />
|
||||
<path d="M10 16a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1l0 -4" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 668 B |
Loading…
Reference in New Issue