Replace the per-key dropdown UI (MapChoiceControl) with a Cairo-rendered
keyboard canvas where users can paint colors directly onto keys.
Editor (lib/solaar/ui/perkey/):
- Cairo DrawingArea renders cells from a Layout dataclass; bound cells
take their painted color, unset cells show a diagonal hash whose base
color matches the device's rgb_zone_* setting.
- Tools: brush, drag-rectangle, flood-fill (4-adjacent, Paint-style),
and a directional gradient (line A->B projected across the matrix
with cells past the endpoints clamped to the endpoint colors).
- GradientSwatch is the single source of truth for the gradient's two
colors; the canvas reads from it on each gradient stroke.
- Palette: GTK ColorButton plus an unset toggle that paints the
"no change" sentinel (-1).
- PerKeyEditorDialog auto-sizes from the canvas's size_request, so a
104-key keyboard opens wide and a 8-LED mouse opens compact.
- Editor consumes only a narrow PerKeyColorSink protocol; never imports
from lib/logitech_receiver, preserving the FE/BE seam.
- Per-device palette state (active + previous color) persists via the
existing persister under a _palette: prefixed key.
Layouts:
- ANSI 104-key full-size and TKL keyboard layouts.
- G502 X family mouse layout (zones 1-8 -> labels A-H).
- Generic registry: register_layout(feature, matcher, layout). A
_name_contains() helper builds case-insensitive substring matchers
against device codename / name. Unknown devices fall back to a flat
strip of all reported zones.
Validator (open value space):
- New Range dataclass and MapRangeValidator extending Validator
directly (kind=MAP_CHOICE for dispatch compatibility). Replaces the
ChoicesMapValidator on PerKeyLighting -- the named-color universe
(COLORSPLUS) was rejecting any picker color outside its ~20 entries.
Other MAP_CHOICE settings are untouched.
Integration:
- Setting base gains an editor_class string attribute. config_panel's
_create_sbox resolves it via importlib before the kind dispatch, so
PerKeyLighting routes to the new editor without a new Kind value.
- CLI gains a hex/dec parser for open-value MAP_CHOICE settings:
solaar config <dev> per-key-lighting A 0xFF00FF
- Diversion rule editor skips Range-valued MAP_CHOICE settings'
value-selector instead of crashing on the open value space.
- pycairo declared in install_requires; transitively present on most
systems but now explicit for pip-from-source installs.
Tests in test_setting_templates.py updated for the new validator.
Warnings found by automatic code inspection and partially tackled
- Drop distuitls inf favour of setuptools
- Replace deprecated pyudev.Device.from_device_number
- Remove unnecessary brackets
- Avoid access to private variables etc.
- Shadows built-in name
- Line length >120 characters
- Not a module level variable
- Simplify clause
and more
* Remove import as _ in solaar startup
Related #2273
* Remove import as _ in listener
Related #2273
* Remove import as _ in cli init
Related #2273
* Remove import as _ in gtk
Related #2273
* Remove import as _ in show
Related #2273
* Remove import as _ in tray
Related #2273
* Remove import as _ in profiles
Related #2273
* Remove import as _ in config
Related #2273
* Remove import as _ in config panel
Related #2273
* Remove import as _ in window
Related #2273
* Remove import as _ in pair
Related #2273
* Remove import as _ in pair window
Related #2273
* Remove import as _ in cli package
Related #2273
* Remove import as _ in ui package
Related #2273
* Remove commented out code
Related #2273
* Use constant for Logitech ID
Replace NamedTuples with more flexible dataclass, which also support
type hints. Introduce enums to replace constant strings, which need to
be kept in sync. Also enhances interfaces by limiting it to the enum
values. Remove unused variables.
* refactor: Create close dialog in its own function
Related #2378
* refactor: Create selected rule edit panel in module level function
Related #2378
* refactor: Remove commented code
Related #2378
* refactor: Use Gdk constant for right click button comparison
Related #2378
* refactor: Make _menu_do_copy a function
Related #2378