Lumi/companion/src/Lumi.Companion.App/HotkeyCaptureBinding.cs
2026-07-29 23:41:34 +02:00

135 lines
3.7 KiB
C#

using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Threading;
namespace Lumi.Companion.App;
public sealed class HotkeyCaptureBinding
{
private readonly Button _button;
private readonly DispatcherTimer _timeout;
private readonly HashSet<Key> _pressed = [];
private string _hotkey = "";
private string _previous = "";
private KeyModifiers _modifiers;
private Key? _primary;
private bool _capturing;
public HotkeyCaptureBinding(Button button)
{
_button = button;
_button.HorizontalContentAlignment = Avalonia.Layout.HorizontalAlignment.Left;
_button.Focusable = true;
_timeout = new DispatcherTimer { Interval = TimeSpan.FromSeconds(7) };
_timeout.Tick += (_, _) => CancelCapture();
_button.Click += (_, _) => BeginCapture();
_button.KeyDown += OnKeyDown;
_button.KeyUp += OnKeyUp;
}
public string Hotkey
{
get => _hotkey;
set
{
_hotkey = string.IsNullOrWhiteSpace(value) ? "Unassigned" : value.Trim();
if (!_capturing) UpdateContent();
}
}
private void OnKeyDown(object? sender, KeyEventArgs e)
{
if (!_capturing)
{
return;
}
e.Handled = true;
if (e.Key == Key.Escape)
{
CancelCapture();
return;
}
_pressed.Add(e.Key);
_modifiers |= e.KeyModifiers;
if (!IsModifier(e.Key)) _primary = e.Key;
_button.Content = _primary is null ? "Hold modifiers, then press a key…" : $"{Format(_modifiers, _primary.Value)} · release to save";
}
private void OnKeyUp(object? sender, KeyEventArgs e)
{
if (!_capturing)
{
return;
}
e.Handled = true;
_pressed.Remove(e.Key);
if (_primary is not null && !_pressed.Any(key => !IsModifier(key)))
{
_hotkey = Format(_modifiers, _primary.Value);
FinishCapture();
}
}
private void BeginCapture()
{
if (_capturing) return;
_capturing = true;
_previous = _hotkey;
_pressed.Clear();
_modifiers = KeyModifiers.None;
_primary = null;
_button.Content = "Press a shortcut…";
_button.Focus();
_timeout.Start();
}
private void CancelCapture()
{
if (!_capturing) return;
_hotkey = _previous;
FinishCapture();
}
private void FinishCapture()
{
_capturing = false;
_timeout.Stop();
_pressed.Clear();
UpdateContent();
}
private void UpdateContent() => _button.Content = $"{_hotkey} (Set hotkey)";
private static bool IsModifier(Key key) => key is
Key.LeftCtrl or Key.RightCtrl or Key.LeftAlt or Key.RightAlt or
Key.LeftShift or Key.RightShift or Key.LWin or Key.RWin;
internal static string Format(KeyModifiers modifiers, Key key)
{
var parts = new List<string>(5);
if (modifiers.HasFlag(KeyModifiers.Control)) parts.Add("Ctrl");
if (modifiers.HasFlag(KeyModifiers.Alt)) parts.Add("Alt");
if (modifiers.HasFlag(KeyModifiers.Shift)) parts.Add("Shift");
if (modifiers.HasFlag(KeyModifiers.Meta)) parts.Add("Win");
parts.Add(KeyName(key));
return string.Join("+", parts);
}
private static string KeyName(Key key) => key switch
{
Key.PageUp => "PageUp",
Key.PageDown => "PageDown",
Key.Left => "Left",
Key.Right => "Right",
Key.Up => "Up",
Key.Down => "Down",
Key.Return => "Enter",
Key.Space => "Space",
Key.Back => "Backspace",
_ => key.ToString()
};
}