Lighting menu in the window for pads that have backlight modes
A backend that lists lighting_modes gets a Lighting button in the footer: one entry per mode and a colour picker for the custom one. The tick follows what the pad reported in its last read, since every read of an ITPROTECH pad carries the lighting along with the keys. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -17,7 +17,7 @@ from PySide6.QtWidgets import (QApplication, QButtonGroup, QFileDialog, QFrame,
|
||||
QGridLayout, QHBoxLayout, QLabel, QLineEdit,
|
||||
QMainWindow, QMessageBox, QPlainTextEdit,
|
||||
QPushButton, QSpinBox, QVBoxLayout, QWidget,
|
||||
QCheckBox, QComboBox, QMenu)
|
||||
QCheckBox, QColorDialog, QComboBox, QMenu)
|
||||
|
||||
from . import __version__, actions as act, core, keymap, padview, themes
|
||||
from .padview import INK, INK_DIM, TAPE, WINDOW, Look, PadView
|
||||
@@ -951,6 +951,20 @@ class Window(QMainWindow):
|
||||
stamp = self.theme_menu.addAction(f"MacroPad {__version__}")
|
||||
stamp.setEnabled(False)
|
||||
self.theme_btn.setMenu(self.theme_menu)
|
||||
# Lighting is pad wide, so it lives down here with the other things
|
||||
# that aren't about one key. Only pads whose backend offers modes
|
||||
# get the button; it is filled in when such a pad turns up.
|
||||
self.light_btn = _button("Lighting", "flat")
|
||||
self.light_btn.setToolTip("Backlight mode and colour, stored on the pad")
|
||||
self.light_menu = QMenu(self)
|
||||
self.light_group = QActionGroup(self)
|
||||
self.light_group.setExclusive(True)
|
||||
self.light_actions = {}
|
||||
self.light_custom = None
|
||||
self.light_backend = None
|
||||
self.light_colour = None
|
||||
self.light_btn.setMenu(self.light_menu)
|
||||
self.light_btn.hide()
|
||||
self.rotate = _button("Rotate pad", "flat")
|
||||
self.import_btn = _button("Import capture…", "flat")
|
||||
self.write_all = _button("", "primary")
|
||||
@@ -961,6 +975,7 @@ class Window(QMainWindow):
|
||||
fl.addWidget(self.edit_btn)
|
||||
fl.addWidget(self.read_btn)
|
||||
fl.addWidget(self.theme_btn)
|
||||
fl.addWidget(self.light_btn)
|
||||
fl.addWidget(self.rotate)
|
||||
fl.addWidget(self.import_btn)
|
||||
fl.addWidget(self.write_all)
|
||||
@@ -984,6 +999,7 @@ class Window(QMainWindow):
|
||||
self.rotate.clicked.connect(self._rotate)
|
||||
self.style_group.triggered.connect(lambda a: self._set_theme(a.data()))
|
||||
self.colour_group.triggered.connect(lambda a: self._set_theme(a.data()))
|
||||
self.light_group.triggered.connect(lambda a: self._set_lighting(a.data()))
|
||||
self.import_btn.clicked.connect(self._import)
|
||||
|
||||
self.note = core._keyd_warning()
|
||||
@@ -1213,8 +1229,10 @@ class Window(QMainWindow):
|
||||
dev = self.diag.device if self.diag else None
|
||||
if not dev:
|
||||
self.inspector.backend = None
|
||||
self._offer_lighting(None)
|
||||
return False
|
||||
self.inspector.backend = core.backend_for(dev)
|
||||
self._offer_lighting(self.inspector.backend)
|
||||
ident = f"{dev.vid}:{dev.pid}"
|
||||
if ident == self.state.device_id:
|
||||
return False
|
||||
@@ -1321,6 +1339,7 @@ class Window(QMainWindow):
|
||||
else:
|
||||
n = len(got)
|
||||
self._say(f"Read {n} control{'s' if n != 1 else ''} from the pad.", GOOD)
|
||||
self._sync_lighting_from_pad()
|
||||
finally:
|
||||
self.read_btn.setEnabled(True)
|
||||
self.read_btn.setText("Read pad")
|
||||
@@ -1403,6 +1422,85 @@ class Window(QMainWindow):
|
||||
self.message.setText(text)
|
||||
self.message.setStyleSheet(f"color: {colour};" if colour else "")
|
||||
|
||||
# ---------------------------------------------------------- lighting
|
||||
|
||||
def _offer_lighting(self, backend):
|
||||
"""Show the Lighting menu for a pad that has modes, hide it otherwise."""
|
||||
modes = getattr(backend, "lighting_modes", None)
|
||||
if not modes:
|
||||
self.light_btn.hide()
|
||||
self.light_backend = None
|
||||
return
|
||||
if backend is not self.light_backend:
|
||||
self.light_backend = backend
|
||||
self.light_menu.clear()
|
||||
for a in list(self.light_group.actions()):
|
||||
self.light_group.removeAction(a)
|
||||
self.light_actions = {}
|
||||
custom = getattr(backend, "lighting_custom", None)
|
||||
for i, name in enumerate(modes):
|
||||
if i == custom:
|
||||
continue
|
||||
a = self.light_menu.addAction(name.capitalize())
|
||||
a.setCheckable(True)
|
||||
a.setData(i)
|
||||
self.light_group.addAction(a)
|
||||
self.light_actions[i] = a
|
||||
self.light_custom = None
|
||||
if custom is not None:
|
||||
self.light_menu.addSeparator()
|
||||
self.light_custom = self.light_menu.addAction("Custom colour…")
|
||||
self.light_custom.setCheckable(True)
|
||||
self.light_custom.setData(custom)
|
||||
self.light_group.addAction(self.light_custom)
|
||||
self.light_actions[custom] = self.light_custom
|
||||
self._sync_lighting_from_pad()
|
||||
self.light_btn.show()
|
||||
|
||||
def _sync_lighting_from_pad(self):
|
||||
"""Tick the mode the pad reported in its last read, if it did."""
|
||||
backend, dev = self.light_backend, self.diag.device if self.diag else None
|
||||
if backend is None or dev is None or not hasattr(backend, "last_lighting"):
|
||||
return
|
||||
got = backend.last_lighting(dev)
|
||||
if got:
|
||||
self._sync_light_menu(*got)
|
||||
|
||||
def _sync_light_menu(self, mode, colour):
|
||||
a = self.light_actions.get(mode)
|
||||
if a is not None:
|
||||
a.setChecked(True)
|
||||
if colour is not None:
|
||||
self.light_colour = tuple(colour)
|
||||
if self.light_custom is not None:
|
||||
hexcol = "#%02x%02x%02x" % self.light_colour if self.light_colour else ""
|
||||
self.light_custom.setText(f"Custom colour… {hexcol}".rstrip())
|
||||
|
||||
def _set_lighting(self, mode, colour=None):
|
||||
"""Send a lighting mode to the pad; the custom one asks for a colour."""
|
||||
backend, dev = self.light_backend, self.diag.device if self.diag else None
|
||||
if backend is None or dev is None or not (self.diag and self.diag.ready):
|
||||
self._say(self.diag.headline if self.diag else "No pad.", BAD)
|
||||
self._sync_lighting_from_pad()
|
||||
return
|
||||
if mode == getattr(backend, "lighting_custom", None) and colour is None:
|
||||
start = QColor(*self.light_colour) if self.light_colour else QColor("white")
|
||||
picked = QColorDialog.getColor(start, self, "Custom colour")
|
||||
if not picked.isValid():
|
||||
self._sync_lighting_from_pad()
|
||||
return
|
||||
colour = (picked.red(), picked.green(), picked.blue())
|
||||
try:
|
||||
backend.set_lighting(dev, mode, colour)
|
||||
except (OSError, ValueError) as e:
|
||||
self._say(f"Couldn't set the lighting: {core._explain(e) if isinstance(e, OSError) else e}", BAD)
|
||||
self._sync_lighting_from_pad()
|
||||
return
|
||||
self._sync_light_menu(mode, colour)
|
||||
name = backend.lighting_modes[mode]
|
||||
extra = " #%02x%02x%02x" % tuple(colour) if colour else ""
|
||||
self._say(f"Lighting: {name}{extra}.", GOOD)
|
||||
|
||||
# ------------------------------------------------------------- misc
|
||||
|
||||
def _edit_layout(self, on, keep=False):
|
||||
|
||||
Reference in New Issue
Block a user