Initial commit
This commit is contained in:
@@ -0,0 +1,441 @@
|
||||
// ActionDialog.cs
|
||||
// Modaler Dialog zum Bearbeiten einer einzelnen Button-/Encoder-Aktion.
|
||||
//
|
||||
// Layout je nach Typ:
|
||||
// HID Tastatur → Capture-Button (Klicken + Taste drücken) + Modifier-Checkboxen
|
||||
// HID Consumer → Dropdown mit benannten Medien-/Lautstärke-Aktionen
|
||||
// Host Command → Zahlen-Eingabe (Command-ID)
|
||||
// Keine Aktion → nichts
|
||||
//
|
||||
// Optional (nur MX-Buttons): LED-Basisfarbe
|
||||
|
||||
namespace VersaGUI;
|
||||
|
||||
public class ActionDialog : Form
|
||||
{
|
||||
// ── Ergebnis ──────────────────────────────────────────────────────────────
|
||||
public DeviceAction ResultAction { get; private set; }
|
||||
public Color ResultColor { get; private set; }
|
||||
|
||||
// ── Controls ──────────────────────────────────────────────────────────────
|
||||
private readonly ComboBox _typeCombo;
|
||||
|
||||
// HID Tastatur
|
||||
private readonly Panel _hidKeyPanel;
|
||||
private readonly Button _captureBtn;
|
||||
private readonly CheckBox _chkCtrl, _chkShift, _chkAlt, _chkWin;
|
||||
|
||||
// HID Consumer
|
||||
private readonly Panel _consumerPanel;
|
||||
private readonly ComboBox _consumerCombo;
|
||||
|
||||
// Host Command
|
||||
private readonly Panel _cmdPanel;
|
||||
private readonly TextBox _cmdBox;
|
||||
|
||||
// LED-Farbe
|
||||
private readonly Panel _colorPanel;
|
||||
private readonly Button _colorBtn;
|
||||
private Color _color;
|
||||
|
||||
// Capture-Zustand
|
||||
private bool _capturing;
|
||||
private byte _hidKeycode; // 0 = nicht belegt
|
||||
|
||||
// ── Lookup-Tabellen ───────────────────────────────────────────────────────
|
||||
|
||||
// Windows VK → HID Keyboard Usage (USB HID Usage Table 0x07)
|
||||
private static readonly Dictionary<Keys, byte> s_vkToHid = new()
|
||||
{
|
||||
// Buchstaben
|
||||
[Keys.A]=0x04,[Keys.B]=0x05,[Keys.C]=0x06,[Keys.D]=0x07,
|
||||
[Keys.E]=0x08,[Keys.F]=0x09,[Keys.G]=0x0A,[Keys.H]=0x0B,
|
||||
[Keys.I]=0x0C,[Keys.J]=0x0D,[Keys.K]=0x0E,[Keys.L]=0x0F,
|
||||
[Keys.M]=0x10,[Keys.N]=0x11,[Keys.O]=0x12,[Keys.P]=0x13,
|
||||
[Keys.Q]=0x14,[Keys.R]=0x15,[Keys.S]=0x16,[Keys.T]=0x17,
|
||||
[Keys.U]=0x18,[Keys.V]=0x19,[Keys.W]=0x1A,[Keys.X]=0x1B,
|
||||
[Keys.Y]=0x1C,[Keys.Z]=0x1D,
|
||||
// Ziffernreihe
|
||||
[Keys.D1]=0x1E,[Keys.D2]=0x1F,[Keys.D3]=0x20,[Keys.D4]=0x21,
|
||||
[Keys.D5]=0x22,[Keys.D6]=0x23,[Keys.D7]=0x24,[Keys.D8]=0x25,
|
||||
[Keys.D9]=0x26,[Keys.D0]=0x27,
|
||||
// Steuerung
|
||||
[Keys.Return]=0x28,[Keys.Escape]=0x29,[Keys.Back]=0x2A,
|
||||
[Keys.Tab]=0x2B, [Keys.Space]=0x2C, [Keys.Delete]=0x4C,
|
||||
[Keys.Insert]=0x49,[Keys.Home]=0x4A, [Keys.PageUp]=0x4B,
|
||||
[Keys.End]=0x4D, [Keys.PageDown]=0x4E,
|
||||
[Keys.Right]=0x4F,[Keys.Left]=0x50, [Keys.Down]=0x51,[Keys.Up]=0x52,
|
||||
[Keys.CapsLock]=0x39,
|
||||
[Keys.PrintScreen]=0x46,[Keys.Scroll]=0x47,[Keys.Pause]=0x48,
|
||||
[Keys.NumLock]=0x53,
|
||||
// F-Tasten
|
||||
[Keys.F1]=0x3A,[Keys.F2]=0x3B,[Keys.F3]=0x3C,[Keys.F4]=0x3D,
|
||||
[Keys.F5]=0x3E,[Keys.F6]=0x3F,[Keys.F7]=0x40,[Keys.F8]=0x41,
|
||||
[Keys.F9]=0x42,[Keys.F10]=0x43,[Keys.F11]=0x44,[Keys.F12]=0x45,
|
||||
// Sonderzeichen (QWERTZ-kompatibel, basiert auf US-HID-Positionen)
|
||||
[Keys.OemMinus]=0x2D, [Keys.Oemplus]=0x2E,
|
||||
[Keys.OemOpenBrackets]=0x2F, [Keys.OemCloseBrackets]=0x30,
|
||||
[Keys.OemPipe]=0x31, [Keys.OemSemicolon]=0x33,
|
||||
[Keys.OemQuotes]=0x34, [Keys.Oemtilde]=0x35,
|
||||
[Keys.Oemcomma]=0x36, [Keys.OemPeriod]=0x37,
|
||||
[Keys.OemQuestion]=0x38,
|
||||
// Numpad
|
||||
[Keys.NumPad1]=0x59,[Keys.NumPad2]=0x5A,[Keys.NumPad3]=0x5B,
|
||||
[Keys.NumPad4]=0x5C,[Keys.NumPad5]=0x5D,[Keys.NumPad6]=0x5E,
|
||||
[Keys.NumPad7]=0x5F,[Keys.NumPad8]=0x60,[Keys.NumPad9]=0x61,
|
||||
[Keys.NumPad0]=0x62,[Keys.Decimal]=0x63,
|
||||
[Keys.Multiply]=0x55,[Keys.Add]=0x57,
|
||||
[Keys.Subtract]=0x56,[Keys.Divide]=0x54,
|
||||
};
|
||||
|
||||
// HID-Code → lesbarer Name (für Capture-Button-Beschriftung)
|
||||
private static readonly Dictionary<byte, string> s_hidNames = new()
|
||||
{
|
||||
[0x04]="A",[0x05]="B",[0x06]="C",[0x07]="D",[0x08]="E",[0x09]="F",
|
||||
[0x0A]="G",[0x0B]="H",[0x0C]="I",[0x0D]="J",[0x0E]="K",[0x0F]="L",
|
||||
[0x10]="M",[0x11]="N",[0x12]="O",[0x13]="P",[0x14]="Q",[0x15]="R",
|
||||
[0x16]="S",[0x17]="T",[0x18]="U",[0x19]="V",[0x1A]="W",[0x1B]="X",
|
||||
[0x1C]="Y",[0x1D]="Z",
|
||||
[0x1E]="1",[0x1F]="2",[0x20]="3",[0x21]="4",[0x22]="5",
|
||||
[0x23]="6",[0x24]="7",[0x25]="8",[0x26]="9",[0x27]="0",
|
||||
[0x28]="Enter", [0x29]="Escape", [0x2A]="Backspace",
|
||||
[0x2B]="Tab", [0x2C]="Leertaste", [0x2D]="-",
|
||||
[0x2E]="=", [0x2F]="[", [0x30]="]",
|
||||
[0x31]="\\", [0x33]=";", [0x34]="'",
|
||||
[0x35]="`", [0x36]=",", [0x37]=".",
|
||||
[0x38]="/", [0x39]="CapsLock",
|
||||
[0x3A]="F1", [0x3B]="F2", [0x3C]="F3", [0x3D]="F4",
|
||||
[0x3E]="F5", [0x3F]="F6", [0x40]="F7", [0x41]="F8",
|
||||
[0x42]="F9", [0x43]="F10",[0x44]="F11",[0x45]="F12",
|
||||
[0x46]="Druck", [0x47]="Rollen", [0x48]="Pause",
|
||||
[0x49]="Einfg", [0x4A]="Pos1", [0x4B]="Bild Auf",
|
||||
[0x4C]="Entf", [0x4D]="Ende", [0x4E]="Bild Ab",
|
||||
[0x4F]="Rechts", [0x50]="Links", [0x51]="Runter", [0x52]="Hoch",
|
||||
[0x53]="NumLock",[0x54]="Num/", [0x55]="Num*",
|
||||
[0x56]="Num-", [0x57]="Num+",
|
||||
[0x59]="Num1", [0x5A]="Num2", [0x5B]="Num3", [0x5C]="Num4",
|
||||
[0x5D]="Num5", [0x5E]="Num6", [0x5F]="Num7", [0x60]="Num8",
|
||||
[0x61]="Num9", [0x62]="Num0", [0x63]="Num.",
|
||||
};
|
||||
|
||||
// HID Consumer Usage IDs (Usage Page 0x0C)
|
||||
private static readonly (ushort Usage, string Name)[] s_consumer =
|
||||
{
|
||||
(0x00CD, "Play / Pause"),
|
||||
(0x00B5, "Nächster Titel"),
|
||||
(0x00B6, "Vorheriger Titel"),
|
||||
(0x00B7, "Stop"),
|
||||
(0x00E9, "Lauter"),
|
||||
(0x00EA, "Leiser"),
|
||||
(0x00E2, "Stummschalten"),
|
||||
(0x0192, "Taschenrechner"),
|
||||
(0x0223, "Browser – Startseite"),
|
||||
(0x0224, "Browser – Zurück"),
|
||||
(0x0225, "Browser – Vor"),
|
||||
(0x00B0, "Aufnahme"),
|
||||
};
|
||||
|
||||
// ── Konstruktor ───────────────────────────────────────────────────────────
|
||||
|
||||
public ActionDialog(DeviceAction action, Color ledColor, bool showColor = true)
|
||||
{
|
||||
ResultAction = action.Clone();
|
||||
ResultColor = ledColor;
|
||||
_color = ledColor;
|
||||
|
||||
// Formular-Grundeinstellungen
|
||||
Text = "Aktion bearbeiten";
|
||||
FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||
StartPosition = FormStartPosition.CenterParent;
|
||||
MinimizeBox = false;
|
||||
MaximizeBox = false;
|
||||
KeyPreview = true; // Tastendrücke abfangen bevor Controls sie kriegen
|
||||
|
||||
// ── Typ-Auswahl ───────────────────────────────────────────────────────
|
||||
var typeLabel = new Label { Text = "Typ:", Location = new Point(12, 16), AutoSize = true };
|
||||
_typeCombo = new ComboBox
|
||||
{
|
||||
Location = new Point(80, 12),
|
||||
Width = 208,
|
||||
DropDownStyle = ComboBoxStyle.DropDownList,
|
||||
};
|
||||
_typeCombo.Items.AddRange(new object[]
|
||||
{ "Keine Aktion", "HID Tastatur", "HID Consumer", "Host Command" });
|
||||
_typeCombo.SelectedIndex = (int)action.Type;
|
||||
_typeCombo.SelectedIndexChanged += OnTypeChanged;
|
||||
|
||||
// ── HID-Tastatur-Panel ────────────────────────────────────────────────
|
||||
_hidKeyPanel = new Panel { Location = new Point(12, 48), Size = new Size(280, 110) };
|
||||
|
||||
_captureBtn = new Button
|
||||
{
|
||||
Size = new Size(280, 48),
|
||||
Location = new Point(0, 0),
|
||||
FlatStyle = FlatStyle.Flat,
|
||||
Font = new Font(Font.FontFamily, 12, FontStyle.Regular),
|
||||
Cursor = Cursors.Hand,
|
||||
};
|
||||
_captureBtn.FlatAppearance.BorderColor = Color.DarkGray;
|
||||
_captureBtn.Click += OnCaptureClick;
|
||||
|
||||
var hint = new Label
|
||||
{
|
||||
Text = "Klicken und dann Taste drücken | Esc = Abbrechen",
|
||||
Location = new Point(0, 54),
|
||||
AutoSize = true,
|
||||
ForeColor = SystemColors.GrayText,
|
||||
Font = new Font(Font.FontFamily, 8),
|
||||
};
|
||||
|
||||
_chkCtrl = new CheckBox { Text = "Strg", Location = new Point(0, 78), AutoSize = true };
|
||||
_chkShift = new CheckBox { Text = "Shift", Location = new Point(60, 78), AutoSize = true };
|
||||
_chkAlt = new CheckBox { Text = "Alt", Location = new Point(120, 78), AutoSize = true };
|
||||
_chkWin = new CheckBox { Text = "Win", Location = new Point(176, 78), AutoSize = true };
|
||||
_chkCtrl.CheckedChanged += (_, _) => RefreshCaptureBtn();
|
||||
_chkShift.CheckedChanged += (_, _) => RefreshCaptureBtn();
|
||||
_chkAlt.CheckedChanged += (_, _) => RefreshCaptureBtn();
|
||||
_chkWin.CheckedChanged += (_, _) => RefreshCaptureBtn();
|
||||
|
||||
// Aus bestehender Aktion befüllen
|
||||
if (action.Type == ActionType.HidKey)
|
||||
{
|
||||
_hidKeycode = (byte)(action.Data & 0xFF);
|
||||
byte mod = (byte)(action.Data >> 8);
|
||||
_chkCtrl.Checked = (mod & 0x11) != 0;
|
||||
_chkShift.Checked = (mod & 0x22) != 0;
|
||||
_chkAlt.Checked = (mod & 0x44) != 0;
|
||||
_chkWin.Checked = (mod & 0x88) != 0;
|
||||
}
|
||||
|
||||
_hidKeyPanel.Controls.AddRange(new Control[]
|
||||
{ _captureBtn, hint, _chkCtrl, _chkShift, _chkAlt, _chkWin });
|
||||
|
||||
RefreshCaptureBtn(); // erst nach Checkbox-Initialisierung aufrufen
|
||||
|
||||
// ── Consumer-Panel ────────────────────────────────────────────────────
|
||||
_consumerPanel = new Panel { Location = new Point(12, 48), Size = new Size(280, 30) };
|
||||
_consumerCombo = new ComboBox
|
||||
{
|
||||
Location = new Point(0, 0),
|
||||
Width = 280,
|
||||
DropDownStyle = ComboBoxStyle.DropDownList,
|
||||
};
|
||||
foreach (var (_, name) in s_consumer)
|
||||
_consumerCombo.Items.Add(name);
|
||||
|
||||
int consumerIdx = action.Type == ActionType.HidConsumer
|
||||
? Array.FindIndex(s_consumer, e => e.Usage == action.Data)
|
||||
: -1;
|
||||
_consumerCombo.SelectedIndex = consumerIdx >= 0 ? consumerIdx : 0;
|
||||
_consumerPanel.Controls.Add(_consumerCombo);
|
||||
|
||||
// ── Host-Command-Panel ────────────────────────────────────────────────
|
||||
_cmdPanel = new Panel { Location = new Point(12, 48), Size = new Size(280, 30) };
|
||||
var cmdLabel = new Label { Text = "Command-ID:", Location = new Point(0, 8), AutoSize = true };
|
||||
_cmdBox = new TextBox
|
||||
{
|
||||
Location = new Point(90, 4),
|
||||
Width = 190,
|
||||
Text = action.Type == ActionType.HostCommand ? $"{action.Data}" : "0",
|
||||
};
|
||||
_cmdPanel.Controls.AddRange(new Control[] { cmdLabel, _cmdBox });
|
||||
|
||||
// ── Farb-Panel ────────────────────────────────────────────────────────
|
||||
_colorPanel = new Panel
|
||||
{
|
||||
Location = new Point(12, 168),
|
||||
Size = new Size(280, 30),
|
||||
Visible = showColor,
|
||||
};
|
||||
var colorLabel = new Label { Text = "LED-Farbe:", Location = new Point(0, 8), AutoSize = true };
|
||||
_colorBtn = new Button
|
||||
{
|
||||
Location = new Point(80, 2),
|
||||
Size = new Size(200, 26),
|
||||
BackColor = ledColor,
|
||||
FlatStyle = FlatStyle.Flat,
|
||||
Text = string.Empty,
|
||||
};
|
||||
_colorBtn.FlatAppearance.BorderSize = 1;
|
||||
_colorBtn.Click += OnColorClick;
|
||||
_colorPanel.Controls.AddRange(new Control[] { colorLabel, _colorBtn });
|
||||
|
||||
// ── Buttons Fußzeile ──────────────────────────────────────────────────
|
||||
int footerY = showColor ? 208 : 168;
|
||||
var okBtn = new Button
|
||||
{
|
||||
Text = "OK",
|
||||
DialogResult = DialogResult.OK,
|
||||
Location = new Point(112, footerY),
|
||||
Width = 80,
|
||||
};
|
||||
okBtn.Click += OnOk;
|
||||
|
||||
var cancelBtn = new Button
|
||||
{
|
||||
Text = "Abbrechen",
|
||||
DialogResult = DialogResult.Cancel,
|
||||
Location = new Point(200, footerY),
|
||||
Width = 92,
|
||||
};
|
||||
|
||||
AcceptButton = okBtn;
|
||||
CancelButton = cancelBtn;
|
||||
ClientSize = new Size(304, footerY + 44);
|
||||
|
||||
Controls.AddRange(new Control[]
|
||||
{
|
||||
typeLabel, _typeCombo,
|
||||
_hidKeyPanel, _consumerPanel, _cmdPanel, _colorPanel,
|
||||
okBtn, cancelBtn,
|
||||
});
|
||||
|
||||
UpdatePanelVisibility();
|
||||
}
|
||||
|
||||
// ── Panel-Sichtbarkeit ────────────────────────────────────────────────────
|
||||
|
||||
private void OnTypeChanged(object? sender, EventArgs e)
|
||||
{
|
||||
StopCapture();
|
||||
UpdatePanelVisibility();
|
||||
}
|
||||
|
||||
private void UpdatePanelVisibility()
|
||||
{
|
||||
var t = (ActionType)_typeCombo.SelectedIndex;
|
||||
_hidKeyPanel.Visible = t == ActionType.HidKey;
|
||||
_consumerPanel.Visible = t == ActionType.HidConsumer;
|
||||
_cmdPanel.Visible = t == ActionType.HostCommand;
|
||||
}
|
||||
|
||||
// ── Tasten-Erfassung ──────────────────────────────────────────────────────
|
||||
|
||||
private void OnCaptureClick(object? sender, EventArgs e)
|
||||
{
|
||||
if (_capturing) StopCapture();
|
||||
else StartCapture();
|
||||
}
|
||||
|
||||
private void StartCapture()
|
||||
{
|
||||
_capturing = true;
|
||||
_captureBtn.Text = "Taste drücken...";
|
||||
_captureBtn.BackColor = Color.FromArgb(255, 220, 80);
|
||||
_captureBtn.ForeColor = Color.Black;
|
||||
_captureBtn.FlatAppearance.BorderColor = Color.DarkOrange;
|
||||
}
|
||||
|
||||
private void StopCapture()
|
||||
{
|
||||
_capturing = false;
|
||||
RefreshCaptureBtn();
|
||||
}
|
||||
|
||||
private void RefreshCaptureBtn()
|
||||
{
|
||||
if (_hidKeycode != 0 && s_hidNames.TryGetValue(_hidKeycode, out string? kn))
|
||||
{
|
||||
var parts = new List<string>();
|
||||
if (_chkCtrl.Checked) parts.Add("Strg");
|
||||
if (_chkShift.Checked) parts.Add("Shift");
|
||||
if (_chkAlt.Checked) parts.Add("Alt");
|
||||
if (_chkWin.Checked) parts.Add("Win");
|
||||
parts.Add(kn);
|
||||
_captureBtn.Text = string.Join(" + ", parts);
|
||||
}
|
||||
else
|
||||
{
|
||||
_captureBtn.Text = "— (klicken zum Erfassen)";
|
||||
}
|
||||
_captureBtn.BackColor = SystemColors.Control;
|
||||
_captureBtn.ForeColor = SystemColors.ControlText;
|
||||
_captureBtn.FlatAppearance.BorderColor = Color.DarkGray;
|
||||
}
|
||||
|
||||
// Tasten werden form-weit abgefangen (KeyPreview = true)
|
||||
protected override void OnKeyDown(KeyEventArgs e)
|
||||
{
|
||||
if (!_capturing) { base.OnKeyDown(e); return; }
|
||||
|
||||
// Reine Modifier-Tasten ignorieren – auf echte Taste warten
|
||||
if (e.KeyCode is Keys.ControlKey or Keys.LControlKey or Keys.RControlKey
|
||||
or Keys.ShiftKey or Keys.LShiftKey or Keys.RShiftKey
|
||||
or Keys.Menu or Keys.LMenu or Keys.RMenu
|
||||
or Keys.LWin or Keys.RWin or Keys.Apps)
|
||||
{
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Escape bricht Erfassung ab (ohne Wert zu ändern)
|
||||
if (e.KeyCode == Keys.Escape)
|
||||
{
|
||||
StopCapture();
|
||||
e.Handled = true;
|
||||
e.SuppressKeyPress = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Modifikatoren übernehmen
|
||||
_chkCtrl.Checked = e.Control;
|
||||
_chkShift.Checked = e.Shift;
|
||||
_chkAlt.Checked = e.Alt;
|
||||
|
||||
// VK → HID-Code
|
||||
_hidKeycode = s_vkToHid.TryGetValue(e.KeyCode, out byte hid) ? hid : (byte)0;
|
||||
|
||||
StopCapture();
|
||||
e.Handled = true;
|
||||
e.SuppressKeyPress = true;
|
||||
}
|
||||
|
||||
// ── Farbe ─────────────────────────────────────────────────────────────────
|
||||
|
||||
private void OnColorClick(object? sender, EventArgs e)
|
||||
{
|
||||
using var dlg = new ColorDialog { Color = _color, FullOpen = true };
|
||||
if (dlg.ShowDialog(this) == DialogResult.OK)
|
||||
{
|
||||
_color = dlg.Color;
|
||||
_colorBtn.BackColor = dlg.Color;
|
||||
}
|
||||
}
|
||||
|
||||
// ── OK ────────────────────────────────────────────────────────────────────
|
||||
|
||||
private void OnOk(object? sender, EventArgs e)
|
||||
{
|
||||
var type = (ActionType)_typeCombo.SelectedIndex;
|
||||
ushort data = 0;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ActionType.HidKey:
|
||||
byte mod = 0;
|
||||
if (_chkCtrl.Checked) mod |= 0x01; // LCtrl
|
||||
if (_chkShift.Checked) mod |= 0x02; // LShift
|
||||
if (_chkAlt.Checked) mod |= 0x04; // LAlt
|
||||
if (_chkWin.Checked) mod |= 0x08; // LGUI
|
||||
data = (ushort)(_hidKeycode | (mod << 8));
|
||||
break;
|
||||
|
||||
case ActionType.HidConsumer:
|
||||
data = s_consumer[_consumerCombo.SelectedIndex].Usage;
|
||||
break;
|
||||
|
||||
case ActionType.HostCommand:
|
||||
if (!ushort.TryParse(_cmdBox.Text.Trim(), out data))
|
||||
{
|
||||
MessageBox.Show("Ungültige Command-ID (0–65535 erwartet).",
|
||||
"Fehler", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
DialogResult = DialogResult.None;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ResultAction = new DeviceAction { Type = type, Data = data };
|
||||
ResultColor = _color;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user