Files
ClassicAssist.Avalonia/ClassicAssist.Shared/Data/Hotkeys/HotkeyManager.cs
T
Beyhan Oğur d5f52cc3d3 first commit
2026-09-03 04:45:36 +03:00

303 lines
8.9 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using ClassicAssist.Annotations;
using ClassicAssist.Data.Hotkeys.Commands;
using ClassicAssist.Data.Macros.Commands;
using ClassicAssist.Misc;
using ClassicAssist.UI.Misc;
using static ClassicAssist.Misc.SDLKeys;
namespace ClassicAssist.Data.Hotkeys
{
public class KeyState
{
[DllImport("SDL2.dll", EntryPoint = "SDL_GetKeyboardState", CallingConvention = CallingConvention.Cdecl)]
private static unsafe extern byte* GetKeyboardState(out int numKeys);
public static unsafe bool GetKeyState(SDL_Scancode scanCode)
{
var result = GetKeyboardState(out int numKeys);
int code = (int) scanCode;
return code < numKeys && result[(int)code] == 1;
}
}
public class HotkeyManager : INotifyPropertyChanged
{
private static HotkeyManager _instance;
private static readonly object _instanceLock = new object();
private readonly object _lock = new object();
private readonly Key[] _modifierKeys =
{
Key.LeftCtrl, Key.RightCtrl, Key.LeftShift, Key.RightShift, Key.LeftAlt, Key.RightAlt
};
private readonly List<Key> _modifiers = new List<Key>();
private bool _enabled = true;
private ObservableCollectionEx<HotkeyCommand> _items = new ObservableCollectionEx<HotkeyCommand>();
private HotkeyManager()
{
}
public Action ClearAllHotkeys { get; set; }
public bool Enabled
{
get => _enabled;
set => SetProperty( ref _enabled, value );
}
public ObservableCollectionEx<HotkeyCommand> Items
{
get => _items;
set => SetProperty( ref _items, value );
}
public event PropertyChangedEventHandler PropertyChanged;
public void AddCategory( HotkeyCommand item, IComparer<HotkeyEntry> comparer = null )
{
if ( Items.Contains( item ) )
{
Items.Remove( item );
}
if ( comparer == null )
{
comparer = Comparer<HotkeyEntry>.Default;
}
int i = 0;
while ( i < Items.Count && comparer.Compare( Items[i], item ) < 0 )
{
i++;
}
Items.Insert( i, item );
}
public void ClearPreviousHotkey( ShortcutKeys keys )
{
foreach ( HotkeyCommand hotkeyEntry in Items )
{
if ( hotkeyEntry.Children == null )
{
continue;
}
foreach ( HotkeyEntry hotkeyEntryChild in hotkeyEntry.Children )
{
if ( Equals( hotkeyEntryChild.Hotkey, keys ) )
{
hotkeyEntryChild.Hotkey = ShortcutKeys.Default;
}
}
}
}
public static HotkeyManager GetInstance()
{
// ReSharper disable once InvertIf
if ( _instance == null )
{
lock ( _instanceLock )
{
if ( _instance == null )
{
_instance = new HotkeyManager();
}
}
}
return _instance;
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged( [CallerMemberName] string propertyName = null )
{
PropertyChanged?.Invoke( this, new PropertyChangedEventArgs( propertyName ) );
}
// ReSharper disable once RedundantAssignment
public void SetProperty<T>( ref T obj, T value, [CallerMemberName] string propertyName = "" )
{
obj = value;
OnPropertyChanged( propertyName );
}
public bool OnHotkeyPressed( Key keys )
{
lock ( _lock )
{
bool filter = false;
if ( _modifierKeys.Contains( keys ) )
{
_modifiers.Clear();
_modifiers.Add( keys );
return false;
}
Key modifier = Key.None;
if ( _modifiers.Count > 0 )
{
modifier = _modifiers[0];
}
bool down = KeyState.GetKeyState(KeysToSDLScancode(modifier));
if ( !down )
{
modifier = Key.None;
}
// Sanity check
if ( keys == Key.None )
{
return false;
}
foreach ( HotkeyCommand hke in Items )
{
if ( hke.Children == null )
{
continue;
}
try
{
IEnumerable<HotkeyEntry> hotkeyEntries = hke.Children.Where( t =>
t.Hotkey.Modifier == modifier && t.Hotkey.Key == keys &&
t.Hotkey.Mouse == MouseOptions.None );
foreach ( HotkeyEntry hks in hotkeyEntries )
{
if ( hks.Disableable && !Enabled )
{
continue;
}
filter = !hks.PassToUO;
AliasCommands.SetDefaultAliases();
Task.Run( () => hks.Action.Invoke( hks ) );
break;
}
}
catch ( InvalidOperationException )
{
// When spamming keys
}
}
return filter;
}
}
private SDL_Scancode KeysToSDLScancode(Key modifier )
{
//TODO Enter keys / find better way
switch ( modifier )
{
case Key.LeftAlt:
return SDL_Scancode.SDL_SCANCODE_LALT;
case Key.Right:
return SDL_Scancode.SDL_SCANCODE_RIGHT;
case Key.LeftCtrl:
return SDL_Scancode.SDL_SCANCODE_LCTRL;
case Key.RightCtrl:
return SDL_Scancode.SDL_SCANCODE_RCTRL;
case Key.LeftShift:
return SDL_Scancode.SDL_SCANCODE_LSHIFT;
case Key.RightShift:
return SDL_Scancode.SDL_SCANCODE_RSHIFT;
}
return 0;
}
public void OnMouseAction( MouseOptions mouse )
{
// Sanity check
if ( mouse == MouseOptions.None )
{
return;
}
lock ( _lock )
{
foreach ( HotkeyCommand hke in Items )
{
if ( hke.Children == null )
{
continue;
}
try
{
Key modifier = Key.None;
//Key modifier = _modifierKeys.FirstOrDefault( key =>
// Engine.Dispatcher.Invoke( () => Keyboard.IsKeyDown( key ) ) );
IEnumerable<HotkeyEntry> hotkeyEntries = hke.Children.Where( t =>
t.Hotkey.Modifier == modifier && t.Hotkey.Key == Key.None && t.Hotkey.Mouse == mouse );
foreach ( HotkeyEntry hks in hotkeyEntries )
{
if ( hks.Disableable && !Enabled )
{
continue;
}
AliasCommands.SetDefaultAliases();
Task.Run( () => hks.Action.Invoke( hks ) );
break;
}
}
catch ( InvalidOperationException )
{
// When spamming wheel
}
}
}
}
public void ClearItems()
{
foreach ( HotkeyEntry entry in Items )
{
ClearHotkeys( entry );
}
Items.Clear();
}
private void ClearHotkeys( HotkeyEntry entry )
{
entry.Hotkey = ShortcutKeys.Default;
if ( !entry.IsCategory )
{
return;
}
foreach ( HotkeyEntry hotkeyEntry in entry.Children )
{
ClearHotkeys( hotkeyEntry );
}
}
}
}