diff --git a/ClipForge/App.xaml b/ClipForge/App.xaml index b3f8d56..d741abd 100644 --- a/ClipForge/App.xaml +++ b/ClipForge/App.xaml @@ -8,9 +8,44 @@ - - + + + + + + + + + + + + + + + + + + + diff --git a/ClipForge/ClipForge.csproj b/ClipForge/ClipForge.csproj index af21841..41618fa 100644 --- a/ClipForge/ClipForge.csproj +++ b/ClipForge/ClipForge.csproj @@ -18,12 +18,12 @@ 4A55954F2A73A9D620442C7DFBFC7C95A71D8D24 SHA256 True - C:\Users\Blade\Desktop\Clipforge Packaged\V1\ + C:\Users\Blade\Desktop\Clipforge Packaged\V0.1\ False True Auto x64 - J:\Projects\ClipForge\ClipForge\bin\x64\Release\net8.0-windows10.0.19041.0 + C:\Users\Blade\Desktop\Clipforge Packaged 0 diff --git a/ClipForge/GlobalHotkeyService.cs b/ClipForge/GlobalHotkeyService.cs index d786b51..551cb5e 100644 --- a/ClipForge/GlobalHotkeyService.cs +++ b/ClipForge/GlobalHotkeyService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.InteropServices; using Microsoft.UI.Xaml; @@ -6,39 +6,49 @@ namespace ClipForge { public class GlobalHotkeyService { - // These two lines talk directly to Windows to register/unregister hotkeys [DllImport("user32.dll")] private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk); [DllImport("user32.dll")] private static extern bool UnregisterHotKey(IntPtr hWnd, int id); - // This is the ID we'll use to identify our clip hotkey private const int HOTKEY_CLIP = 1; + private const uint MOD_NOREPEAT = 0x4000; // don't fire on key repeat - // Alt key modifier - private const uint MOD_ALT = 0x0001; - - // F9 key - private const uint VK_F9 = 0x78; - - // This is the "event" that fires when the hotkey is pressed public event Action? ClipRequested; private IntPtr _hwnd; + private uint _modifiers; + private uint _vk; - public void Initialize(IntPtr hwnd) + /// Register the clip hotkey. Use settings values (e.g. HotkeyModifiers, HotkeyVirtualKey). + public void Initialize(IntPtr hwnd, uint modifiers, uint vk) { _hwnd = hwnd; - RegisterHotKey(_hwnd, HOTKEY_CLIP, MOD_ALT, VK_F9); + _modifiers = modifiers; + _vk = vk; + TryRegister(); + } + + /// Update the hotkey at runtime (e.g. after user remaps). Unregisters old and registers new. + public bool UpdateHotkey(uint modifiers, uint vk) + { + UnregisterHotKey(_hwnd, HOTKEY_CLIP); + _modifiers = modifiers; + _vk = vk; + return TryRegister(); + } + + private bool TryRegister() + { + uint mod = _modifiers | MOD_NOREPEAT; + return RegisterHotKey(_hwnd, HOTKEY_CLIP, mod, _vk); } public void ProcessHotkey(int id) { if (id == HOTKEY_CLIP) - { ClipRequested?.Invoke(); - } } public void Cleanup() diff --git a/ClipForge/HotkeyHelper.cs b/ClipForge/HotkeyHelper.cs new file mode 100644 index 0000000..8b88a08 --- /dev/null +++ b/ClipForge/HotkeyHelper.cs @@ -0,0 +1,183 @@ +using System; +using System.Collections.Generic; +using Windows.System; + +namespace ClipForge +{ + /// Converts between hotkey modifier/vk and display strings. + public static class HotkeyHelper + { + public const uint MOD_ALT = 0x0001; + public const uint MOD_CONTROL = 0x0002; + public const uint MOD_SHIFT = 0x0004; + public const uint MOD_WIN = 0x0008; + public const uint MOD_NOREPEAT = 0x4000; + + // Built in static ctor with TryAdd so duplicate VirtualKey enum values (e.g. Kana/Hangul = 0x15) don't throw. + private static readonly Dictionary KeyNames = new(); + + static HotkeyHelper() + { + TryAdd(VirtualKey.LeftButton, "LMB"); + TryAdd(VirtualKey.RightButton, "RMB"); + TryAdd(VirtualKey.Cancel, "Cancel"); + TryAdd(VirtualKey.Back, "Backspace"); + TryAdd(VirtualKey.Tab, "Tab"); + TryAdd(VirtualKey.Clear, "Clear"); + TryAdd(VirtualKey.Enter, "Enter"); + TryAdd(VirtualKey.Shift, "Shift"); + TryAdd(VirtualKey.Control, "Ctrl"); + TryAdd(VirtualKey.Menu, "Alt"); + TryAdd(VirtualKey.Pause, "Pause"); + TryAdd(VirtualKey.CapitalLock, "Caps Lock"); + TryAdd(VirtualKey.Kana, "Kana"); + TryAdd(VirtualKey.Hangul, "Hangul"); // same value as Kana on some SDKs; TryAdd skips duplicate + TryAdd(VirtualKey.Junja, "Junja"); + TryAdd(VirtualKey.Final, "Final"); + TryAdd(VirtualKey.Hanja, "Hanja"); + TryAdd(VirtualKey.Kanji, "Kanji"); + TryAdd(VirtualKey.Escape, "Escape"); + TryAdd(VirtualKey.Convert, "Convert"); + TryAdd(VirtualKey.NonConvert, "NonConvert"); + TryAdd(VirtualKey.Accept, "Accept"); + TryAdd(VirtualKey.ModeChange, "ModeChange"); + TryAdd(VirtualKey.Space, "Space"); + TryAdd(VirtualKey.PageUp, "PgUp"); + TryAdd(VirtualKey.PageDown, "PgDn"); + TryAdd(VirtualKey.End, "End"); + TryAdd(VirtualKey.Home, "Home"); + TryAdd(VirtualKey.Left, "Left"); + TryAdd(VirtualKey.Up, "Up"); + TryAdd(VirtualKey.Right, "Right"); + TryAdd(VirtualKey.Down, "Down"); + TryAdd(VirtualKey.Select, "Select"); + TryAdd(VirtualKey.Print, "Print"); + TryAdd(VirtualKey.Execute, "Execute"); + TryAdd(VirtualKey.Snapshot, "Print Screen"); + TryAdd(VirtualKey.Insert, "Insert"); + TryAdd(VirtualKey.Delete, "Delete"); + TryAdd(VirtualKey.Help, "Help"); + TryAdd(VirtualKey.Number0, "0"); + TryAdd(VirtualKey.Number1, "1"); + TryAdd(VirtualKey.Number2, "2"); + TryAdd(VirtualKey.Number3, "3"); + TryAdd(VirtualKey.Number4, "4"); + TryAdd(VirtualKey.Number5, "5"); + TryAdd(VirtualKey.Number6, "6"); + TryAdd(VirtualKey.Number7, "7"); + TryAdd(VirtualKey.Number8, "8"); + TryAdd(VirtualKey.Number9, "9"); + TryAdd(VirtualKey.A, "A"); + TryAdd(VirtualKey.B, "B"); + TryAdd(VirtualKey.C, "C"); + TryAdd(VirtualKey.D, "D"); + TryAdd(VirtualKey.E, "E"); + TryAdd(VirtualKey.F, "F"); + TryAdd(VirtualKey.G, "G"); + TryAdd(VirtualKey.H, "H"); + TryAdd(VirtualKey.I, "I"); + TryAdd(VirtualKey.J, "J"); + TryAdd(VirtualKey.K, "K"); + TryAdd(VirtualKey.L, "L"); + TryAdd(VirtualKey.M, "M"); + TryAdd(VirtualKey.N, "N"); + TryAdd(VirtualKey.O, "O"); + TryAdd(VirtualKey.P, "P"); + TryAdd(VirtualKey.Q, "Q"); + TryAdd(VirtualKey.R, "R"); + TryAdd(VirtualKey.S, "S"); + TryAdd(VirtualKey.T, "T"); + TryAdd(VirtualKey.U, "U"); + TryAdd(VirtualKey.V, "V"); + TryAdd(VirtualKey.W, "W"); + TryAdd(VirtualKey.X, "X"); + TryAdd(VirtualKey.Y, "Y"); + TryAdd(VirtualKey.Z, "Z"); + TryAdd(VirtualKey.LeftWindows, "Win"); + TryAdd(VirtualKey.RightWindows, "Win"); + TryAdd(VirtualKey.Application, "App"); + TryAdd(VirtualKey.Sleep, "Sleep"); + TryAdd(VirtualKey.NumberPad0, "Num 0"); + TryAdd(VirtualKey.NumberPad1, "Num 1"); + TryAdd(VirtualKey.NumberPad2, "Num 2"); + TryAdd(VirtualKey.NumberPad3, "Num 3"); + TryAdd(VirtualKey.NumberPad4, "Num 4"); + TryAdd(VirtualKey.NumberPad5, "Num 5"); + TryAdd(VirtualKey.NumberPad6, "Num 6"); + TryAdd(VirtualKey.NumberPad7, "Num 7"); + TryAdd(VirtualKey.NumberPad8, "Num 8"); + TryAdd(VirtualKey.NumberPad9, "Num 9"); + TryAdd(VirtualKey.Multiply, "Num *"); + TryAdd(VirtualKey.Add, "Num +"); + TryAdd(VirtualKey.Separator, "Num ,"); + TryAdd(VirtualKey.Subtract, "Num -"); + TryAdd(VirtualKey.Decimal, "Num ."); + TryAdd(VirtualKey.Divide, "Num /"); + TryAdd(VirtualKey.F1, "F1"); + TryAdd(VirtualKey.F2, "F2"); + TryAdd(VirtualKey.F3, "F3"); + TryAdd(VirtualKey.F4, "F4"); + TryAdd(VirtualKey.F5, "F5"); + TryAdd(VirtualKey.F6, "F6"); + TryAdd(VirtualKey.F7, "F7"); + TryAdd(VirtualKey.F8, "F8"); + TryAdd(VirtualKey.F9, "F9"); + TryAdd(VirtualKey.F10, "F10"); + TryAdd(VirtualKey.F11, "F11"); + TryAdd(VirtualKey.F12, "F12"); + TryAdd(VirtualKey.F13, "F13"); + TryAdd(VirtualKey.F14, "F14"); + TryAdd(VirtualKey.F15, "F15"); + TryAdd(VirtualKey.F16, "F16"); + TryAdd(VirtualKey.F17, "F17"); + TryAdd(VirtualKey.F18, "F18"); + TryAdd(VirtualKey.F19, "F19"); + TryAdd(VirtualKey.F20, "F20"); + TryAdd(VirtualKey.F21, "F21"); + TryAdd(VirtualKey.F22, "F22"); + TryAdd(VirtualKey.F23, "F23"); + TryAdd(VirtualKey.F24, "F24"); + TryAdd(VirtualKey.NumberKeyLock, "NumLock"); + TryAdd(VirtualKey.Scroll, "Scroll Lock"); + TryAdd(VirtualKey.LeftShift, "Shift"); + TryAdd(VirtualKey.RightShift, "Shift"); + TryAdd(VirtualKey.LeftControl, "Ctrl"); + TryAdd(VirtualKey.RightControl, "Ctrl"); + TryAdd(VirtualKey.LeftMenu, "Alt"); + TryAdd(VirtualKey.RightMenu, "Alt"); + } + + private static void TryAdd(VirtualKey key, string name) + { + KeyNames.TryAdd(key, name); + } + + /// Format modifier flags + virtual key as display string (e.g. "Alt + PgUp"). + public static string ToDisplayString(uint modifiers, uint vk) + { + var parts = new List(); + if ((modifiers & MOD_WIN) != 0) parts.Add("Win"); + if ((modifiers & MOD_CONTROL) != 0) parts.Add("Ctrl"); + if ((modifiers & MOD_ALT) != 0) parts.Add("Alt"); + if ((modifiers & MOD_SHIFT) != 0) parts.Add("Shift"); + var keyName = GetKeyName((VirtualKey)vk); + if (!string.IsNullOrEmpty(keyName)) + parts.Add(keyName); + return parts.Count > 0 ? string.Join(" + ", parts) : "None"; + } + + public static string GetKeyName(VirtualKey key) + { + return KeyNames.TryGetValue(key, out var name) ? name : key.ToString(); + } + + /// True if the key is typically used only as a modifier (don't use as sole key). + public static bool IsModifierKey(VirtualKey key) + { + return key == VirtualKey.LeftWindows || key == VirtualKey.RightWindows + || key == VirtualKey.Control || key == VirtualKey.LeftControl || key == VirtualKey.RightControl + || key == VirtualKey.Menu || key == VirtualKey.LeftMenu || key == VirtualKey.RightMenu + || key == VirtualKey.Shift || key == VirtualKey.LeftShift || key == VirtualKey.RightShift; + } + } +} diff --git a/ClipForge/MainWindow.xaml b/ClipForge/MainWindow.xaml index 3e3959c..bf013f1 100644 --- a/ClipForge/MainWindow.xaml +++ b/ClipForge/MainWindow.xaml @@ -13,56 +13,71 @@ - + - + - + - - - + + + + + + + + + - + @@ -91,7 +112,7 @@ - + @@ -102,37 +123,44 @@ - + + FontSize="26" + FontWeight="Bold" + Foreground="{StaticResource ClipForgeTextPrimaryBrush}"/> + Margin="0,0,0,4"/>