From de1d241dd71accc47f107fcf2f92273132ee10a2 Mon Sep 17 00:00:00 2001 From: Frank van der Stam Date: Mon, 9 Sep 2024 14:54:31 +0200 Subject: [PATCH 1/3] Hotkey picker --- .../UI/EldenRing/EldenRingControl.xaml | 4 +- src/SoulSplitter/UI/Generic/HotkeyPicker.xaml | 48 +++++++ .../UI/Generic/HotkeyPicker.xaml.cs | 130 ++++++++++++++++++ src/cli/Program.cs | 2 + 4 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 src/SoulSplitter/UI/Generic/HotkeyPicker.xaml create mode 100644 src/SoulSplitter/UI/Generic/HotkeyPicker.xaml.cs diff --git a/src/SoulSplitter/UI/EldenRing/EldenRingControl.xaml b/src/SoulSplitter/UI/EldenRing/EldenRingControl.xaml index a1e16d55..fd5c8326 100644 --- a/src/SoulSplitter/UI/EldenRing/EldenRingControl.xaml +++ b/src/SoulSplitter/UI/EldenRing/EldenRingControl.xaml @@ -108,8 +108,10 @@ + + diff --git a/src/SoulSplitter/UI/Generic/HotkeyPicker.xaml b/src/SoulSplitter/UI/Generic/HotkeyPicker.xaml new file mode 100644 index 00000000..fb109049 --- /dev/null +++ b/src/SoulSplitter/UI/Generic/HotkeyPicker.xaml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/SoulSplitter/UI/Generic/HotkeyPicker.xaml.cs b/src/SoulSplitter/UI/Generic/HotkeyPicker.xaml.cs new file mode 100644 index 00000000..a906562c --- /dev/null +++ b/src/SoulSplitter/UI/Generic/HotkeyPicker.xaml.cs @@ -0,0 +1,130 @@ +// This file is part of the SoulSplitter distribution (https://github.com/FrankvdStam/SoulSplitter). +// Copyright (c) 2022 Frank van der Stam. +// https://github.com/FrankvdStam/SoulSplitter/blob/main/LICENSE +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System.Collections.Generic; +using System.Globalization; +using System.Windows.Forms; +using System.Windows.Input; +using SoulSplitter.UI.EldenRing; +using KeyEventArgs = System.Windows.Input.KeyEventArgs; +using TextBox = System.Windows.Controls.TextBox; +using UserControl = System.Windows.Controls.UserControl; + +namespace SoulSplitter.UI.Generic +{ + /// + /// Interaction logic for HotkeyPicker.xaml + /// + public partial class HotkeyPicker : UserControl + { + public HotkeyPicker() + { + InitializeComponent(); + } + + public class HotkeyCompletedParameter + { + public object Sender; + public ModifierKeys ModifierKeys; + public Key Key; + } + + public RelayCommand HotkeyCompletedCommand { get; set; } + + private bool _isActive = false; + private ModifierKeys _modifierKeys = ModifierKeys.None; + private Key _key = Key.None; + private void OnPreviewKeyDown(object sender, KeyEventArgs e) + { + if (_isActive) + { + if (e.IsDown && sender is TextBox textBox) + { + //Get modifier key and remember state + if (_modifierKeysLookup.TryGetValue(e.Key, out ModifierKeys modifierKeys)) + { + if (!_modifierKeys.HasFlag(modifierKeys)) + { + if (!string.IsNullOrWhiteSpace(textBox.Text)) + { + textBox.Text += " + "; + } + + textBox.Text += modifierKeys; + _modifierKeys |= modifierKeys; + } + } + else //get regular key, end listening for keypresses, invoke command + { + if (!string.IsNullOrWhiteSpace(textBox.Text)) + { + textBox.Text += " + "; + } + + textBox.Text += e.Key; + _key = e.Key; + + HotkeyCompletedCommand?.Execute(new HotkeyCompletedParameter + { + Sender = this, + Key = _key, + ModifierKeys = _modifierKeys + }); + _isActive = false; + } + + var converter = new ModifierKeysConverter(); + e.Handled = true; + } + } + } + + private void OnPreviewMouseDown(object sender, MouseButtonEventArgs e) + { + if (!_isActive && sender is TextBox textBox) + { + _isActive = true; + _key = Key.None; + _modifierKeys = ModifierKeys.None; + textBox.Text = ""; + } + } + + private static Dictionary _modifierKeysLookup = new Dictionary() + { + { Key.LeftAlt, ModifierKeys.Alt }, + { Key.RightAlt, ModifierKeys.Alt }, + { Key.System, ModifierKeys.Alt }, + { Key.LeftCtrl, ModifierKeys.Control }, + { Key.RightCtrl, ModifierKeys.Control }, + { Key.LeftShift, ModifierKeys.Shift }, + { Key.RightShift, ModifierKeys.Shift }, + { Key.LWin, ModifierKeys.Windows }, + { Key.RWin, ModifierKeys.Windows }, + }; + + private void OnPreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) + { + if (_isActive && sender is TextBox textBox) + { + _isActive = false; + _key = Key.None; + _modifierKeys = ModifierKeys.None; + textBox.Text = ""; + } + } + } +} diff --git a/src/cli/Program.cs b/src/cli/Program.cs index 5e9271e0..f4f06a6a 100644 --- a/src/cli/Program.cs +++ b/src/cli/Program.cs @@ -41,6 +41,8 @@ internal class Program [STAThread] static void Main(string[] args) { + TestUi(); + GlobalHotKey.RegisterHotKey(ModifierKeys.Alt, Key.A, () =>{ Debug.WriteLine("A"); }); GlobalHotKey.RegisterHotKey(ModifierKeys.Alt, Key.S, () =>{ Debug.WriteLine("S"); }); GlobalHotKey.RegisterHotKey(ModifierKeys.Alt, Key.D, () =>{ Debug.WriteLine("D"); }); From 8e9bc47fcd7dae545725d47c160115a4f4a0ff4d Mon Sep 17 00:00:00 2001 From: Frank van der Stam Date: Tue, 24 Sep 2024 10:33:33 +0200 Subject: [PATCH 2/3] Exclude .net standard lib from license check --- qodana.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qodana.yaml b/qodana.yaml index 25055d2d..8576a059 100644 --- a/qodana.yaml +++ b/qodana.yaml @@ -8,4 +8,7 @@ exclude: - name: exclude paths: - src/cli - - tests \ No newline at end of file + - tests + +dependencyIgnores: + - name: "NETStandard.Library" \ No newline at end of file From 99ec4ff9e55faf17d470cf45707689ca21168dcf Mon Sep 17 00:00:00 2001 From: Frank van der Stam Date: Sat, 2 Nov 2024 17:10:20 +0100 Subject: [PATCH 3/3] WIP hotkey stuff --- src/SoulSplitter/Hotkeys/GlobalHotKey.cs | 16 +- src/SoulSplitter/Hotkeys/Hotkey.cs | 31 + .../UI/Converters/HotkeyToStringConverter.cs | 59 ++ .../UI/EldenRing/EldenRingControl.xaml | 985 ++++++++++-------- .../UI/EldenRing/EldenRingViewModel.cs | 20 + .../UI/EldenRing/FpsPatchHotkey.cs | 39 + src/cli/Program.cs | 6 +- 7 files changed, 700 insertions(+), 456 deletions(-) create mode 100644 src/SoulSplitter/Hotkeys/Hotkey.cs create mode 100644 src/SoulSplitter/UI/Converters/HotkeyToStringConverter.cs create mode 100644 src/SoulSplitter/UI/EldenRing/FpsPatchHotkey.cs diff --git a/src/SoulSplitter/Hotkeys/GlobalHotKey.cs b/src/SoulSplitter/Hotkeys/GlobalHotKey.cs index f87c0a6e..18986238 100644 --- a/src/SoulSplitter/Hotkeys/GlobalHotKey.cs +++ b/src/SoulSplitter/Hotkeys/GlobalHotKey.cs @@ -26,7 +26,7 @@ namespace SoulSplitter.Hotkeys { public static class GlobalHotKey { - private static readonly List<(int id, ModifierKeys modifier, Key key, Action action)> Hotkeys = new List<(int, ModifierKeys, Key, Action)>(); + private static readonly List<(int id, Hotkey hotkey, Action action)> Hotkeys = new List<(int, Hotkey, Action)>(); private static readonly ManualResetEvent WindowReadyEvent = new ManualResetEvent(false); public static volatile HotkeyForm HotkeyForm; public static volatile IntPtr Handle; @@ -43,22 +43,22 @@ static GlobalHotKey() messageLoopThread.Start(); } - public static void OnHotkeyPressed(ModifierKeys modifier, Key key) + public static void OnHotkeyPressed(Hotkey hotkey) { Hotkeys.ForEach(x => { - if (modifier == x.modifier && key == x.key) + if (hotkey.Modifiers == x.hotkey.Modifiers && hotkey.Key == x.hotkey.Key) { x.action(); } }); } - public static int RegisterHotKey(ModifierKeys modifier, Key key, Action action) + public static int RegisterHotKey(Hotkey hotkey, Action action) { WindowReadyEvent.WaitOne(); //wait for hotkey window to have initialized - var virtualKeyCode = (Keys)KeyInterop.VirtualKeyFromKey(key); + var virtualKeyCode = (Keys)KeyInterop.VirtualKeyFromKey(hotkey.Key); int id = Interlocked.Increment(ref _currentId); Delegate register = (Action)(() => @@ -66,12 +66,12 @@ public static int RegisterHotKey(ModifierKeys modifier, Key key, Action action) User32.RegisterHotkey( Handle, id, - (uint)modifier | 0x4000, //no repeat + (uint)hotkey.Modifiers | 0x4000, //no repeat (uint)virtualKeyCode); }); HotkeyForm.Invoke(register); - Hotkeys.Add((id, modifier, key, action)); + Hotkeys.Add((id, hotkey, action)); return id; } @@ -109,7 +109,7 @@ protected override void WndProc(ref Message windowMessage) { var key = KeyInterop.KeyFromVirtualKey((int)windowMessage.LParam >> 16 & 0xFFFF); var modifier = (ModifierKeys)((int)windowMessage.LParam & 0xFFFF); - GlobalHotKey.OnHotkeyPressed(modifier, key); + GlobalHotKey.OnHotkeyPressed(new Hotkey(){Modifiers = modifier, Key = key}); } } diff --git a/src/SoulSplitter/Hotkeys/Hotkey.cs b/src/SoulSplitter/Hotkeys/Hotkey.cs new file mode 100644 index 00000000..39ac277d --- /dev/null +++ b/src/SoulSplitter/Hotkeys/Hotkey.cs @@ -0,0 +1,31 @@ +// This file is part of the SoulSplitter distribution (https://github.com/FrankvdStam/SoulSplitter). +// Copyright (c) 2022 Frank van der Stam. +// https://github.com/FrankvdStam/SoulSplitter/blob/main/LICENSE +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Input; + +namespace SoulSplitter.Hotkeys +{ + public class Hotkey + { + public ModifierKeys Modifiers { get; set; } + public Key Key { get; set; } + } +} diff --git a/src/SoulSplitter/UI/Converters/HotkeyToStringConverter.cs b/src/SoulSplitter/UI/Converters/HotkeyToStringConverter.cs new file mode 100644 index 00000000..e6842591 --- /dev/null +++ b/src/SoulSplitter/UI/Converters/HotkeyToStringConverter.cs @@ -0,0 +1,59 @@ +// This file is part of the SoulSplitter distribution (https://github.com/FrankvdStam/SoulSplitter). +// Copyright (c) 2022 Frank van der Stam. +// https://github.com/FrankvdStam/SoulSplitter/blob/main/LICENSE +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using SoulSplitter.Hotkeys; +using SoulSplitter.UI.EldenRing; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; +using System.Windows.Input; + +namespace SoulSplitter.UI.Converters +{ + public class HotkeyToStringConverter : IValueConverter + { + private static List _modifiers = Enum.GetValues(typeof(ModifierKeys)).Cast().Where(i => i != ModifierKeys.None).ToList(); + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is Hotkey hotkey) + { + var sb = new StringBuilder(); + foreach (var modifier in _modifiers) + { + if (hotkey.Modifiers.HasFlag(modifier)) + { + sb.Append(modifier); + sb.Append(" + "); + } + } + sb.Append(hotkey.Key); + return sb.ToString(); + } + + throw new ArgumentException($"{value} is not a {nameof(Hotkey)}", nameof(value)); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return ""; + } + } +} diff --git a/src/SoulSplitter/UI/EldenRing/EldenRingControl.xaml b/src/SoulSplitter/UI/EldenRing/EldenRingControl.xaml index fd5c8326..0a9d0124 100644 --- a/src/SoulSplitter/UI/EldenRing/EldenRingControl.xaml +++ b/src/SoulSplitter/UI/EldenRing/EldenRingControl.xaml @@ -45,6 +45,7 @@ + @@ -94,464 +95,558 @@ - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/src/SoulSplitter/UI/EldenRing/EldenRingViewModel.cs b/src/SoulSplitter/UI/EldenRing/EldenRingViewModel.cs index 1b71d260..84f0c655 100644 --- a/src/SoulSplitter/UI/EldenRing/EldenRingViewModel.cs +++ b/src/SoulSplitter/UI/EldenRing/EldenRingViewModel.cs @@ -20,8 +20,10 @@ using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; +using System.Windows.Input; using System.Xml.Serialization; using SoulMemory.EldenRing; +using SoulSplitter.Hotkeys; using SoulSplitter.Splits.EldenRing; using SoulSplitter.UI.Generic; @@ -31,6 +33,9 @@ public class EldenRingViewModel : INotifyPropertyChanged { public EldenRingViewModel() { + Hotkeys.Add(new FpsPatchHotkey(){ Action = FpsPatchHotkeyAction.SetFpsValue, Hotkey = new Hotkey(){ Key = Key.A, Modifiers = ModifierKeys.Control }, Fps = 33}); + Hotkeys.Add(new FpsPatchHotkey(){ Action = FpsPatchHotkeyAction.SetFpsValue, Hotkey = new Hotkey(){ Key = Key.B, Modifiers = ModifierKeys.Shift }, Fps = 24}); + Hotkeys.Add(new FpsPatchHotkey(){ Action = FpsPatchHotkeyAction.RemoveFpsPatch, Hotkey = new Hotkey(){ Key = Key.C, Modifiers = ModifierKeys.Alt }}); } public bool StartAutomatically @@ -440,7 +445,22 @@ public void RestoreHierarchy() #endregion + #region Fps hotkeys + [XmlIgnore] + public int NewFpsValue + { + get => _newFpsValue; + set => SetField(ref _newFpsValue, value); + } + private int _newFpsValue = 30; + + public ObservableCollection Hotkeys { get; set; } = new ObservableCollection(); + + + + + #endregion public ObservableCollection Splits { get; set; }= new ObservableCollection(); //source lists diff --git a/src/SoulSplitter/UI/EldenRing/FpsPatchHotkey.cs b/src/SoulSplitter/UI/EldenRing/FpsPatchHotkey.cs new file mode 100644 index 00000000..bb2ae951 --- /dev/null +++ b/src/SoulSplitter/UI/EldenRing/FpsPatchHotkey.cs @@ -0,0 +1,39 @@ +// This file is part of the SoulSplitter distribution (https://github.com/FrankvdStam/SoulSplitter). +// Copyright (c) 2022 Frank van der Stam. +// https://github.com/FrankvdStam/SoulSplitter/blob/main/LICENSE +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Input; +using SoulSplitter.Hotkeys; + +namespace SoulSplitter.UI.EldenRing +{ + public enum FpsPatchHotkeyAction + { + SetFpsValue, + RemoveFpsPatch, + } + + public class FpsPatchHotkey + { + public int Fps { get; set; } + public Hotkey Hotkey { get; set; } + public FpsPatchHotkeyAction Action { get; set; } + } +} diff --git a/src/cli/Program.cs b/src/cli/Program.cs index f4f06a6a..294cb2c7 100644 --- a/src/cli/Program.cs +++ b/src/cli/Program.cs @@ -43,9 +43,9 @@ static void Main(string[] args) { TestUi(); - GlobalHotKey.RegisterHotKey(ModifierKeys.Alt, Key.A, () =>{ Debug.WriteLine("A"); }); - GlobalHotKey.RegisterHotKey(ModifierKeys.Alt, Key.S, () =>{ Debug.WriteLine("S"); }); - GlobalHotKey.RegisterHotKey(ModifierKeys.Alt, Key.D, () =>{ Debug.WriteLine("D"); }); + //GlobalHotKey.RegisterHotKey(ModifierKeys.Alt, Key.A, () =>{ Debug.WriteLine("A"); }); + //GlobalHotKey.RegisterHotKey(ModifierKeys.Alt, Key.S, () =>{ Debug.WriteLine("S"); }); + //GlobalHotKey.RegisterHotKey(ModifierKeys.Alt, Key.D, () =>{ Debug.WriteLine("D"); }); //TestUi(); GameLoop((e) =>