Add files via upload
This commit is contained in:
@@ -8,7 +8,8 @@ import sys
|
||||
from pathlib import Path
|
||||
|
||||
from PySide6.QtCore import QEvent, QSettings, Qt, QTimer, Signal
|
||||
from PySide6.QtGui import QColor, QFont, QFontDatabase, QGuiApplication, QPalette
|
||||
from PySide6.QtGui import (QColor, QFont, QFontDatabase, QGuiApplication, QIcon,
|
||||
QPalette)
|
||||
from PySide6.QtWidgets import (QApplication, QButtonGroup, QFileDialog, QFrame,
|
||||
QGridLayout, QHBoxLayout, QLabel, QLineEdit,
|
||||
QMainWindow, QMessageBox, QPlainTextEdit,
|
||||
@@ -201,9 +202,11 @@ class Inspector(QWidget):
|
||||
modes.setSpacing(6)
|
||||
self.mode_keys = _button("Shortcut", checkable=True)
|
||||
self.mode_media = _button("Media key", checkable=True)
|
||||
self.mode_none = _button("Nothing", checkable=True)
|
||||
self.mode_none.setToolTip("Make this control do nothing at all")
|
||||
self.mode_group = QButtonGroup(self)
|
||||
self.mode_group.setExclusive(True)
|
||||
for i, b in enumerate((self.mode_keys, self.mode_media)):
|
||||
for i, b in enumerate((self.mode_keys, self.mode_media, self.mode_none)):
|
||||
self.mode_group.addButton(b, i)
|
||||
modes.addWidget(b)
|
||||
modes.addStretch()
|
||||
@@ -239,9 +242,12 @@ class Inspector(QWidget):
|
||||
self.media_group.addButton(b, [m for m, _ in core.MEDIA].index(mid))
|
||||
self.media_buttons[mid] = b
|
||||
ml.addWidget(b, r, col)
|
||||
self.pages = (kp, mp)
|
||||
np_ = _label("Pressing or turning it will do nothing. Handy for a key "
|
||||
"you keep hitting by accident.", "dim", wrap=True)
|
||||
self.pages = (kp, mp, np_)
|
||||
ev.addWidget(kp)
|
||||
ev.addWidget(mp)
|
||||
ev.addWidget(np_)
|
||||
|
||||
ev.addSpacing(10)
|
||||
actions = QHBoxLayout()
|
||||
@@ -325,7 +331,12 @@ class Inspector(QWidget):
|
||||
self.onpad_why.hide()
|
||||
|
||||
shown = desired or (None if entry.unknown else entry.binding)
|
||||
if shown is not None and shown.media:
|
||||
if shown is not None and shown.none:
|
||||
self.mode_none.setChecked(True)
|
||||
self._page(2)
|
||||
self.keys_edit.clear()
|
||||
self._clear_media()
|
||||
elif shown is not None and shown.media:
|
||||
self.mode_media.setChecked(True)
|
||||
self._page(1)
|
||||
self.media_buttons[shown.media].setChecked(True)
|
||||
@@ -357,6 +368,8 @@ class Inspector(QWidget):
|
||||
|
||||
def _mode(self, i):
|
||||
self._page(i)
|
||||
if i == 2 and not self._loading and self.control is not None:
|
||||
self.changed.emit(self.control, core.Binding(none=True))
|
||||
if i == 0 and self.record.isChecked():
|
||||
self.keys_edit.setFocus()
|
||||
|
||||
@@ -484,7 +497,7 @@ class Window(QMainWindow):
|
||||
elif e.unknown:
|
||||
looks[c] = Look("", known=False)
|
||||
else:
|
||||
looks[c] = Look(e.binding.label(), known=True)
|
||||
looks[c] = Look(e.binding.label(), known=True, quiet=e.binding.none)
|
||||
self.pad.set_looks(looks)
|
||||
|
||||
n = len(pending)
|
||||
@@ -569,7 +582,10 @@ class Window(QMainWindow):
|
||||
|
||||
if len(done) == 1:
|
||||
c, b = done[0]
|
||||
self._say(f"{core.control_name(c)} is now {b.label()}.", GOOD)
|
||||
if b.none:
|
||||
self._say(f"{core.control_name(c)} now does nothing.", GOOD)
|
||||
else:
|
||||
self._say(f"{core.control_name(c)} is now {b.label()}.", GOOD)
|
||||
else:
|
||||
self._say(f"Wrote {len(done)} changes to the pad.", GOOD)
|
||||
self._refresh()
|
||||
@@ -637,10 +653,17 @@ def apply_theme(app):
|
||||
app.setStyleSheet(STYLE)
|
||||
|
||||
|
||||
ICON = Path(__file__).resolve().parent.parent / "packaging" / "macropad.svg"
|
||||
|
||||
|
||||
def run():
|
||||
app = QApplication(sys.argv)
|
||||
app.setApplicationName("macropad")
|
||||
app.setDesktopFileName("macropad")
|
||||
# The launcher gives Plasma the icon; this covers the window itself, X11
|
||||
# sessions, and running straight from a git checkout.
|
||||
if ICON.exists():
|
||||
app.setWindowIcon(QIcon(str(ICON)))
|
||||
apply_theme(app)
|
||||
w = Window()
|
||||
w.resize(1000, 800)
|
||||
|
||||
@@ -144,6 +144,7 @@ class Binding:
|
||||
Binding(keys="ctrl+c")
|
||||
Binding(keys="a,b,c", delay=50)
|
||||
Binding(media="volumeup")
|
||||
Binding(none=True) the control does nothing
|
||||
|
||||
The kind is explicit rather than guessed from the string, because
|
||||
'pause' is both a keyboard key and a media key.
|
||||
@@ -151,17 +152,18 @@ class Binding:
|
||||
keys: str = ""
|
||||
media: str = ""
|
||||
delay: int = 0
|
||||
none: bool = False
|
||||
|
||||
def validate(self):
|
||||
if bool(self.keys) == bool(self.media):
|
||||
raise ValueError("a binding is either keys or media, not both or neither")
|
||||
if sum(map(bool, (self.keys, self.media, self.none))) != 1:
|
||||
raise ValueError("a binding is exactly one of keys, media or nothing")
|
||||
if self.media and self.media not in proto.MEDIA_KEYS:
|
||||
raise ValueError(f"unknown media key '{self.media}'")
|
||||
if self.keys:
|
||||
proto.parse_keystroke(self.keys) # raises ValueError
|
||||
if not 0 <= self.delay <= 0xFFFF:
|
||||
raise ValueError("delay must be 0-65535 ms")
|
||||
if self.media and self.delay:
|
||||
if self.delay and not self.keys:
|
||||
raise ValueError("delay only applies to key sequences")
|
||||
return self
|
||||
|
||||
@@ -169,9 +171,18 @@ class Binding:
|
||||
def steps(self):
|
||||
return len(proto.parse_keystroke(self.keys)) if self.keys else 1
|
||||
|
||||
@property
|
||||
def is_nothing(self):
|
||||
return self.none
|
||||
|
||||
def reports(self, report_id, action, layer=1):
|
||||
"""The exact reports to send, built entirely by macropad.py."""
|
||||
self.validate()
|
||||
if self.none:
|
||||
# A keystroke with no modifier and no key: count 0, pair 00 00.
|
||||
# This is ch57x-keyboard-tool's encoding for a modifier-only key,
|
||||
# with no modifier. Needs confirming on hardware (see README).
|
||||
return proto.native_key(report_id, action, [(0, 0)], layer, 0)
|
||||
if self.media:
|
||||
return proto.native_media(report_id, action,
|
||||
proto.MEDIA_KEYS[self.media], layer)
|
||||
@@ -185,12 +196,16 @@ class Binding:
|
||||
|
||||
def label(self):
|
||||
"""What a person reads on the key: 'Ctrl+Shift+N', 'Volume up'."""
|
||||
if self.none:
|
||||
return "Nothing"
|
||||
if self.media:
|
||||
return MEDIA_LABEL.get(self.media, self.media)
|
||||
text = ", ".join(pretty_step(s) for s in self.keys.split(",") if s.strip())
|
||||
return text + (f" ({self.delay} ms)" if self.delay else "")
|
||||
|
||||
def to_json(self):
|
||||
if self.none:
|
||||
return {"none": True}
|
||||
if self.media:
|
||||
return {"media": self.media}
|
||||
d = {"keys": self.keys}
|
||||
@@ -201,7 +216,8 @@ class Binding:
|
||||
@classmethod
|
||||
def from_json(cls, d):
|
||||
return cls(keys=d.get("keys", ""), media=d.get("media", ""),
|
||||
delay=int(d.get("delay", 0))).validate()
|
||||
delay=int(d.get("delay", 0)),
|
||||
none=bool(d.get("none", False))).validate()
|
||||
|
||||
|
||||
# ------------------------------------------------------------------- files
|
||||
@@ -426,8 +442,10 @@ def diagnose(keyd=True):
|
||||
"worked out. Pads in this family differ in layout and "
|
||||
"protocol, so writing to yours with the wrong one could "
|
||||
"fail or scramble it. The app won't try.",
|
||||
"You can help add it: the README's \"Other pads\" section "
|
||||
"shows how to capture what the vendor software sends."],
|
||||
"ch57x-keyboard-tool, a command line tool, supports several "
|
||||
"of these pads today. To get yours into this app, the "
|
||||
"README's \"Other pads\" section shows how to capture what "
|
||||
"the vendor software sends."],
|
||||
warnings=warnings)
|
||||
|
||||
if not found:
|
||||
@@ -570,6 +588,12 @@ def decode_config(report):
|
||||
media = _MEDIA_BY_USAGE.get(report[11] | (report[12] << 8))
|
||||
return (control, layer, Binding(media=media)) if media else None
|
||||
|
||||
if kind == proto.KeyType.BASIC and count == 0:
|
||||
mods, code = report[11], report[12]
|
||||
if not mods and not code:
|
||||
return control, layer, Binding(none=True)
|
||||
return None
|
||||
|
||||
if kind == proto.KeyType.BASIC and count:
|
||||
steps = []
|
||||
for i in range(count):
|
||||
|
||||
@@ -43,6 +43,7 @@ class Look:
|
||||
text: str = "" # what's on the pad, or will be once written
|
||||
known: bool = False
|
||||
pending: bool = False
|
||||
quiet: bool = False # known, but does nothing: legend drawn dimmed
|
||||
|
||||
|
||||
def _key_grid(orientation):
|
||||
@@ -268,7 +269,7 @@ class PadView(QWidget):
|
||||
if look.pending:
|
||||
p.setPen(TAPE)
|
||||
else:
|
||||
p.setPen(INK if look.known else INK_DIM)
|
||||
p.setPen(INK if look.known and not look.quiet else INK_DIM)
|
||||
text = look.text if (look.known or look.pending) else "Unknown"
|
||||
body = rect.adjusted(8, 22, -8, -10)
|
||||
# Let 'Meta+Ctrl+Left' break after a '+' instead of being cut off.
|
||||
@@ -379,7 +380,8 @@ class PadView(QWidget):
|
||||
f = QFont(self.font())
|
||||
f.setPixelSize(12)
|
||||
p.setFont(f)
|
||||
p.setPen(TAPE if look.pending else (INK if look.known else INK_DIM))
|
||||
p.setPen(TAPE if look.pending else
|
||||
(INK if look.known and not look.quiet else INK_DIM))
|
||||
text = look.text if (look.known or look.pending) else "Unknown"
|
||||
body = rect.adjusted(22, 0, -26, 0)
|
||||
p.drawText(body, Qt.AlignLeft | Qt.AlignVCenter,
|
||||
|
||||
Reference in New Issue
Block a user