Document key capture, dialog placement and copy/paste

AGENTS.md: die Regel "Auswahl ist ein Dropdown, kein Tastendruck-Capture"
ist ueberholt -- Capture gibt es jetzt, aber weiterhin ohne WinAPI-Hook, und
genau diese Grenze muss bleiben. Dazu die Layout-Naeherung (Y/Z vertauscht
auf deutschem Layout, minus-Kollision bewusst zugunsten der US-Position
aufgeloest), die neuen UI-Regeln (Dialogposition, Grab-Rueckgabe bei
verschachtelten Dialogen, feste Panel-Hoehe, In-Memory-Ablage fuers
Kopieren) und ein Verifikationsrezept fuer UI-Aenderungen inkl. der
DPI-Falle beim Screenshot-Vergleich. Der Deferred-Work-Eintrag zum Capture
faellt weg.

README.md: neue Bedienung in den Features, Tastenkuerzel-Uebersicht, und die
Einschraenkung praezisiert (nicht mehr "Dropdown statt Capture", sondern
was die Fenster-basierte Erkennung nicht sehen kann).

docs/architecture.md: _ModalDialog/_KeyCapture in der UI-Schicht, plus zwei
neue Abschnitte zu den Grenzen der Erkennung und zur Kopier-Ablage.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Julian Appel 2026-08-28 15:33:38 +02:00
parent 21f9cbc2ed
commit 8a00b3ae36
3 changed files with 152 additions and 12 deletions

View file

@ -93,7 +93,17 @@ auf.
umschaltbaren Modi (siehe „Modi" unten), Tray-Icon, Info-Dialog mit
MCP-Doku.
- **`action_dialog.py`** — modale Bearbeiten-Dialoge (`ActionEditDialog`,
`MacroStepsDialog`) für den Programmiermodus.
`MacroStepsDialog`) für den Programmiermodus, auf gemeinsamer Basis
`_ModalDialog`:
- positioniert sich beim Öffnen mittig über dem aufrufenden Fenster
(Aufbau `withdraw()`n, `_center_on_parent()`, dann `deiconify()`);
- bindet `<KeyPress>`/`<KeyRelease>` auf dem Toplevel und verteilt sie:
entweder an eine laufende Tastendruck-Aufnahme, sonst als
Enter = OK / Escape = Abbrechen;
- `_KeyCapture` schaltet ein Label in den Aufnahmemodus und schickt jeden
Tastendruck durch `versapad_data.tk_event_to_hid()` — reine
Tk-Fenster-Events, **kein globaler Tastaturhook** (siehe „Grenzen der
Tastendruck-Erkennung" unten).
### MCP-Server
@ -145,6 +155,40 @@ Drei Checkboxen, unabhängig voneinander:
| Live-Sync | Fragt per Serial das aktuell aktive Profil ab, schaltet die Ansicht mit | Hintergrund-Thread pollt `read_active_profile()`, hält dafür den COM-Port dauerhaft offen, solange die Checkbox an ist. Schließt sich mit VersaGUI/Programmiermodus/MCP-Board-Zugriff gegenseitig aus (exklusiver Port). |
| Programmiermodus | Zellen anklicken zum Bearbeiten | Lädt `self.combined` **einmalig pro Prozesslauf** beim ersten Aktivieren (bevorzugt `DEFAULT_PATH`, sonst `default_combined()`). Jede Bearbeitung speichert sofort automatisch (`_autosave_combined()`). **Achtung:** Da der Snapshot nur einmal geladen wird, sieht der Programmiermodus externe Änderungen (z.B. per MCP) erst nach einem Neustart der exe oder einem expliziten „Datei laden…“. |
### Grenzen der Tastendruck-Erkennung
Die Erkennung im Bearbeiten-Dialog nutzt ausschließlich Tk-Events des
fokussierten Fensters. Daraus folgt zweierlei, und beides ist bewusst so:
1. **Nur was das Fenster erreicht, wird erkannt.** Win+L, Strg+Alt+Entf und
andere vom Betriebssystem abgefangene Kombinationen kommen nie an. Ein
globaler `SetWindowsHookEx`-Hook würde sie sehen, ist aber ausgeschlossen
(AV-Fehlalarm-Risiko, siehe `AGENTS.md`).
2. **Die Zuordnung ist eine US-Layout-Näherung.** HID-Keycodes bezeichnen
physische Tastenpositionen des US-Layouts; Tk liefert nur `keysym` und
Windows-Virtual-Key-Code, beide vom *aktiven* Layout abgeleitet. Die
physische Position (Scan-Code) wäre nötig, um das exakt aufzulösen, und
ist ohne WinAPI-Aufruf nicht verfügbar. Praktische Folge auf deutschem
Layout: Y und Z landen vertauscht auf dem Board. Das Ergebnis wird immer
ins Dropdown und in die Modifier-Checkboxen geschrieben und ist dort
korrigierbar — die Erkennung ersetzt die manuelle Auswahl nicht, sie
beschleunigt sie nur.
Details der Zuordnungstabellen: `versapad_data.tk_event_to_hid()` und die
`_TK_*`/`_WIN_VK_TO_HID`-Dicts darüber.
### Kopieren/Einfügen zwischen Tasten
Rechtsklick auf eine Karte im Programmiermodus (bzw. `Strg+C`/`Strg+V` auf
der Karte unter dem Mauszeiger) kopiert Belegung und/oder LED-Farbe auf
andere Tasten. Die Ablage ist eine reine In-Memory-Struktur in
`desktop_viewer.VersaPadViewer._clip` (`{"action": …, "led": …}`), **nicht**
die System-Zwischenablage — dort lägen nur Textrepräsentationen, hier
werden ganze Action-Dicts übertragen. Eingefügt wird immer eine
`copy.deepcopy()`, damit zwei Tasten nicht dasselbe Dict teilen. Encoder
haben keine eigene LED; eine kopierte Farbe auf einen Encoder einzufügen ist
deshalb wirkungslos.
Tk-Aufrufe passieren nie direkt aus dem Serial- oder Tray-Hintergrundthread
— Ergebnisse landen in einer `queue.Queue`, der Main-Thread holt sie per
`after()`-Polling ab (Absturzrisiko bei Cross-Thread-Tk-Zugriff, siehe