135 lines
3.7 KiB
C#
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()
|
|
};
|
|
}
|