diff --git a/src/Riter/App.xaml.cs b/src/Riter/App.xaml.cs index 919a781..adcf585 100644 --- a/src/Riter/App.xaml.cs +++ b/src/Riter/App.xaml.cs @@ -53,7 +53,6 @@ protected override void OnStartup(StartupEventArgs e) private static void ConfigureServices(ServiceCollection serviceCollection) { - serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/src/Riter/Core/AppSettings.cs b/src/Riter/Core/AppSettings.cs index 4a5a458..b8e090f 100644 --- a/src/Riter/Core/AppSettings.cs +++ b/src/Riter/Core/AppSettings.cs @@ -33,5 +33,5 @@ public class AppSettings /// public const string MyTelegram = "https://t.me/mhakarimi"; - public IEnumerable HotKeysConfig { get; set; } + public List HotKeysConfig { get; set; } } diff --git a/src/Riter/Core/HotKey/GlobalHotkeyManager.cs b/src/Riter/Core/HotKey/GlobalHotkeyManager.cs deleted file mode 100644 index be56ed6..0000000 --- a/src/Riter/Core/HotKey/GlobalHotkeyManager.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Windows.Interop; - -namespace Riter.Core; - -/// -/// Provides a mechanism for registering and managing multiple global hotkeys in a WPF application. -/// Allows triggering specific actions using keyboard shortcuts, even when the application is not in focus. -/// -public partial class GlobalHotKeyManager : IDisposable -{ - private readonly IntPtr _windowHandle; - private readonly HwndSource _source; - private readonly Dictionary> _hotkeyActions = []; - - private bool _isHookAdded; - - /// - /// Initializes a new instance of the class for the specified WPF window. - /// - /// The WPF window that the global hotkeys will be tied to. - public GlobalHotKeyManager(Window window) - { - _windowHandle = new WindowInteropHelper(window).Handle; - _source = HwndSource.FromHwnd(_windowHandle); - } - - public bool RegisterHotkey(HotKey id, uint modifiers, uint key, Action callback) - { - if (!_isHookAdded) - { - _source.AddHook(HwndHook); - _isHookAdded = true; - } - - var registered = RegisterHotKey(_windowHandle, (int)id, modifiers, key); - if (registered) - { - _hotkeyActions[id] = callback; - } - - return registered; - } - - - /// - /// Unregisters a hotkey by its unique identifier. - /// - /// The unique identifier of the hotkey to unregister. - public void UnregisterHotkey(HotKey id) - { - if (_hotkeyActions.ContainsKey(id)) - { - UnregisterHotKey(_windowHandle, (int)id); - _hotkeyActions.Remove(id); - } - } - - /// - /// Releases all resources used by the class. - /// Unregisters all registered hotkeys and removes the window hook. - /// - public void Dispose() - { - foreach (var id in _hotkeyActions.Keys) - { - UnregisterHotKey(_windowHandle, (int)id); - } - - _source.RemoveHook(HwndHook); - _hotkeyActions.Clear(); - } - - [LibraryImport("user32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - private static partial bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk); - - [LibraryImport("user32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - private static partial bool UnregisterHotKey(IntPtr hWnd, int id); - - [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] - private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); - - /// - /// Processes window messages for registered hotkeys. - /// Invokes the associated callback action when a hotkey is pressed. - /// - /// The handle to the window receiving the message. - /// The message identifier. - /// wParam Additional message information. - /// lParam Additional message information. - /// Indicates whether the message has been handled. - /// The return value is zero if the message is handled. - private IntPtr HwndHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) - { - const int wM_HOTKEY = 0x0312; - - if (msg == wM_HOTKEY) - { - var id = (HotKey)wParam.ToInt32(); - - if (_hotkeyActions.TryGetValue(id, out var value)) - { - value?.Invoke(id); - handled = true; - } - } - - // Call the next hook in the chain, allowing other applications to receive the keystroke - return CallNextHookEx(IntPtr.Zero, msg, wParam, lParam); - } -} diff --git a/src/Riter/Core/HotKey/HotKey.cs b/src/Riter/Core/HotKey/HotKey.cs index 1a261dd..c784033 100644 --- a/src/Riter/Core/HotKey/HotKey.cs +++ b/src/Riter/Core/HotKey/HotKey.cs @@ -1,4 +1,7 @@ -namespace Riter.Core; +using System.Runtime.InteropServices; +using System.Text; + +namespace Riter.Core; public enum HotKey { Drawing = 9000, @@ -27,3 +30,5 @@ public enum HotKey Gray = 9022, Black = 9023 } + +public record struct HotKeiesPressed(string Key, bool CtrlPressed, bool ShiftPressed); diff --git a/src/Riter/Core/HotKey/HotKeysConfig.cs b/src/Riter/Core/HotKey/HotKeysConfig.cs index 70be3a7..2705f80 100644 --- a/src/Riter/Core/HotKey/HotKeysConfig.cs +++ b/src/Riter/Core/HotKey/HotKeysConfig.cs @@ -1,15 +1,8 @@ namespace Riter.Core; -public class Value -{ - public string Modifiers { get; init; } - - public string Key { get; init; } -} - public class HotKeysConfig { - public string Key { get; set; } + public string Key { get; init; } - public Value Value { get; set; } + public string Value { get; init; } } diff --git a/src/Riter/Core/HotKey/HotkeyLoader.cs b/src/Riter/Core/HotKey/HotkeyLoader.cs deleted file mode 100644 index 909d5b4..0000000 --- a/src/Riter/Core/HotKey/HotkeyLoader.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Riter.ViewModel; - -namespace Riter.Core; - -public class HotKeyLoader(AppSettings options) -{ - public const uint CTRL = 0x0002; - public const uint SHIFT = 0x0004; - public const uint ALT = 0x0001; - - public Dictionary callback)> Loads(PaletteStateOrchestratorViewModel viewModel) - { - Dictionary callback)> hotkeys = []; - foreach (var hotkey in options.HotKeysConfig) - { - var hotKey = System.Enum.Parse(hotkey.Key); - var modifier = GetModifierKey(hotkey.Value.Modifiers); - var key = GetKeyValue(hotkey.Value.Key); - - hotkeys[hotKey] = (modifier, key, viewModel.HandleHotkey); - } - - return hotkeys; - } - - private static uint GetModifierKey(string modifier) => modifier switch - { - "CTRL" => CTRL, - "SHIFT" => SHIFT, - "ALT" => ALT, - _ => 0 - }; - - private static uint GetKeyValue(string key) => key.ToUpper()[0]; -} diff --git a/src/Riter/Core/HotKey/KeyConverter.cs b/src/Riter/Core/HotKey/KeyConverter.cs new file mode 100644 index 0000000..ea1c40d --- /dev/null +++ b/src/Riter/Core/HotKey/KeyConverter.cs @@ -0,0 +1,48 @@ +using System.Runtime.InteropServices; +using System.Text; + +namespace Riter.Core; + +using System; +using System.Runtime.InteropServices; +using System.Text; +using System.Windows.Input; + +public static class KeyConverter +{ + [DllImport("user32.dll")] + private static extern int ToUnicode( + uint wVirtKey, + uint wScanCode, + byte[] lpKeyState, + [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszBuff, + int cchBuff, + uint wFlags); + + [DllImport("user32.dll")] + private static extern bool GetKeyboardState(byte[] lpKeyState); + + [DllImport("user32.dll")] + private static extern uint MapVirtualKey(uint uCode, uint uMapType); + + public static string VkCodeToString(int vkCode) + { + var keyState = new byte[256]; + if (!GetKeyboardState(keyState)) + { + return string.Empty; + } + + // Ensure the shift key state is set if necessary + if ((vkCode >= 'A' && vkCode <= 'Z') || (vkCode >= '0' && vkCode <= '9')) + { + keyState[(int)Key.LeftShift] = 0x80; + } + + var scanCode = MapVirtualKey((uint)vkCode, 0); + var sb = new StringBuilder(5); + int result = ToUnicode((uint)vkCode, scanCode, keyState, sb, sb.Capacity, 0); + + return result > 0 ? sb.ToString() : string.Empty; + } +} diff --git a/src/Riter/MainWindow.xaml.cs b/src/Riter/MainWindow.xaml.cs index 6ce0eb2..6eb07ba 100644 --- a/src/Riter/MainWindow.xaml.cs +++ b/src/Riter/MainWindow.xaml.cs @@ -1,7 +1,11 @@ -using System.Windows.Controls; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Windows.Controls; using System.Windows.Ink; +using System.Windows.Input; using Riter.Core.Extensions; using Riter.Core.Interfaces; +using Riter.Core.UI; using Riter.ViewModel; namespace Riter; @@ -11,21 +15,41 @@ namespace Riter; /// public partial class MainWindow : Window { + private const int WHKEYBOARDLL = 13; + private const int WMKEYDOWN = 0x0100; + private const int WMKEYUP = 0x0101; + + private static readonly LowLevelKeyboardProc _proc = HookCallback; + private static IntPtr _hookID = IntPtr.Zero; + private static bool _ctrlPressed = false; + private static bool _shiftPressed = false; + private static PaletteStateOrchestratorViewModel _orchestratorViewModel; + private readonly IStrokeHistoryService _strokeHistoryService; - private readonly HotKeyLoader _hotKeyLoader; - private readonly PaletteStateOrchestratorViewModel _orchestratorViewModel; - private GlobalHotKeyManager _globalHotkeyManager; - - public MainWindow( - PaletteStateOrchestratorViewModel orchestratorViewModel, - HotKeyLoader hotKeyLoader, - IStrokeHistoryService strokeHistoryService) + + private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId); + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool UnhookWindowsHookEx(IntPtr hhk); + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); + + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern IntPtr GetModuleHandle(string lpModuleName); + + public MainWindow(PaletteStateOrchestratorViewModel orchestratorViewModel, + IStrokeHistoryService strokeHistoryService) { InitializeComponent(); DataContext = orchestratorViewModel; _orchestratorViewModel = orchestratorViewModel; _strokeHistoryService = strokeHistoryService; - _hotKeyLoader = hotKeyLoader; + // _hotKeyLoader = hotKeyLoader; _strokeHistoryService.SetMainElementToRedoAndUndo(MainInkCanvasControl.MainInkCanvas); MainInkCanvasControl.MainInkCanvas.Strokes.StrokesChanged += StrokesChanged; @@ -35,28 +59,69 @@ public MainWindow( Loaded += MainWindow_Loaded; } + protected override void OnClosed(EventArgs e) + { + base.OnClosed(e); + UnhookWindowsHookEx(_hookID); + } + /// protected override void OnSourceInitialized(EventArgs e) { base.OnSourceInitialized(e); - _globalHotkeyManager = new GlobalHotKeyManager(this); - var hotkies = _hotKeyLoader.Loads(_orchestratorViewModel); + _hookID = SetHook(_proc); + } - foreach (var hotkey in hotkies) - { - _globalHotkeyManager.RegisterHotkey( - hotkey.Key, - hotkey.Value.modifiers, - hotkey.Value.key, - hotkey.Value.callback); - } + private static IntPtr SetHook(LowLevelKeyboardProc proc) + { + using var curProcess = Process.GetCurrentProcess(); + using var curModule = curProcess.MainModule; + return SetWindowsHookEx(WHKEYBOARDLL, proc, GetModuleHandle(curModule.ModuleName), 0); } - /// - protected override void OnClosed(EventArgs e) + private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) { - _globalHotkeyManager.Dispose(); - base.OnClosed(e); + var isKey = true; + if (nCode >= 0 && (wParam == WMKEYDOWN || wParam == WMKEYUP)) + { + var vkCode = Marshal.ReadInt32(lParam); + + if (wParam is WMKEYDOWN) + { + if (vkCode == KeyInterop.VirtualKeyFromKey(Key.LeftCtrl) || vkCode == KeyInterop.VirtualKeyFromKey(Key.RightCtrl)) + { + _ctrlPressed = true; + isKey = false; + } + + if (vkCode == KeyInterop.VirtualKeyFromKey(Key.LeftShift) || vkCode == KeyInterop.VirtualKeyFromKey(Key.RightShift)) + { + _shiftPressed = true; + isKey = false; + } + + if (isKey) + { + _orchestratorViewModel.HandleHotkey(new HotKeiesPressed(KeyInterop.KeyFromVirtualKey(vkCode).ToString(), _ctrlPressed, _shiftPressed)); + _ctrlPressed = false; + _shiftPressed = false; + } + } + else if (wParam == WMKEYUP) + { + if (vkCode == KeyInterop.VirtualKeyFromKey(Key.LeftCtrl) || vkCode == KeyInterop.VirtualKeyFromKey(Key.RightCtrl)) + { + _ctrlPressed = false; + } + + if (vkCode == KeyInterop.VirtualKeyFromKey(Key.LeftShift) || vkCode == KeyInterop.VirtualKeyFromKey(Key.RightShift)) + { + _shiftPressed = false; + } + } + } + + return CallNextHookEx(_hookID, nCode, wParam, lParam); } /// diff --git a/src/Riter/Riter.csproj b/src/Riter/Riter.csproj index ee092bb..6cf6e9f 100644 --- a/src/Riter/Riter.csproj +++ b/src/Riter/Riter.csproj @@ -6,7 +6,7 @@ disable enable true - 0.0.12 + 0.0.13 diff --git a/src/Riter/ViewModel/PaletteStateOrchestratorViewModel.cs b/src/Riter/ViewModel/PaletteStateOrchestratorViewModel.cs index 353197d..d147b3a 100644 --- a/src/Riter/ViewModel/PaletteStateOrchestratorViewModel.cs +++ b/src/Riter/ViewModel/PaletteStateOrchestratorViewModel.cs @@ -5,6 +5,9 @@ namespace Riter.ViewModel; public class PaletteStateOrchestratorViewModel : BaseViewModel { + private readonly AppSettings _appSettings; + private readonly Dictionary _hotKeyCommandMap = []; + public PaletteStateOrchestratorViewModel( DrawingViewModel drawingViewModel, StrokeVisibilityViewModel strokeVisibilityViewModel, @@ -13,8 +16,10 @@ public PaletteStateOrchestratorViewModel( InkEditingModeViewModel inkEditingModeViewModel, HighlighterViewModel highlighterViewModel, SettingPanelViewModel settingPanelViewModel, - ButtonSelectedViewModel buttonSelectedViewModel) + ButtonSelectedViewModel buttonSelectedViewModel, + AppSettings appSettings) { + _appSettings = appSettings; DrawingViewModel = drawingViewModel; StrokeVisibilityViewModel = strokeVisibilityViewModel; StrokeHistoryViewModel = strokeHistoryViewModel; @@ -24,6 +29,34 @@ public PaletteStateOrchestratorViewModel( SettingPanelViewModel = settingPanelViewModel; ButtonSelectedViewModel = buttonSelectedViewModel; + _hotKeyCommandMap = new() + { + { HotKey.Drawing, () => DrawingViewModel.StartDrawingCommand.Execute(null) }, + { HotKey.Erasing, () => DrawingViewModel.StartErasingCommand.Execute(null) }, + { HotKey.Trash, () => StrokeHistoryViewModel.ClearCommand.Execute(null) }, + { HotKey.Highlighter, () => DrawingViewModel.ToggleHighlighterCommand.Execute(null) }, + { HotKey.Release, () => DrawingViewModel.ReleaseCommand.Execute(null) }, + { HotKey.HideAll, () => StrokeVisibilityViewModel.HideAllCommand.Execute(null) }, + { HotKey.Undo, () => StrokeHistoryViewModel.UndoCommand.Execute(null) }, + { HotKey.Redo, () => StrokeHistoryViewModel.RedoCommand.Execute(null) }, + { HotKey.SizeOfBrush1X, () => BrushSettingsViewModel.SetSizeOfBrushWithHotKeyCommand.Execute(BrushSize.X1) }, + { HotKey.SizeOfBrush2X, () => BrushSettingsViewModel.SetSizeOfBrushWithHotKeyCommand.Execute(BrushSize.X2) }, + { HotKey.SizeOfBrush3X, () => BrushSettingsViewModel.SetSizeOfBrushWithHotKeyCommand.Execute(BrushSize.X3) }, + { HotKey.Yellow, () => BrushSettingsViewModel.SetInkColorWithHotKeyCommand.Execute(InkColor.Yellow) }, + { HotKey.Purple, () => BrushSettingsViewModel.SetInkColorWithHotKeyCommand.Execute(InkColor.Purple) }, + { HotKey.Mint, () => BrushSettingsViewModel.SetInkColorWithHotKeyCommand.Execute(InkColor.Mint) }, + { HotKey.Coral, () => BrushSettingsViewModel.SetInkColorWithHotKeyCommand.Execute(InkColor.Coral) }, + { HotKey.Red, () => BrushSettingsViewModel.SetInkColorWithHotKeyCommand.Execute(InkColor.Red) }, + { HotKey.Cyan, () => BrushSettingsViewModel.SetInkColorWithHotKeyCommand.Execute(InkColor.Cyan) }, + { HotKey.White, () => BrushSettingsViewModel.SetInkColorWithHotKeyCommand.Execute(InkColor.White) }, + { HotKey.Orange, () => BrushSettingsViewModel.SetInkColorWithHotKeyCommand.Execute(InkColor.Orange) }, + { HotKey.Gray, () => BrushSettingsViewModel.SetInkColorWithHotKeyCommand.Execute(InkColor.Gray) }, + { HotKey.Black, () => BrushSettingsViewModel.SetInkColorWithHotKeyCommand.Execute(InkColor.Black) }, + { HotKey.TransparentBackground, () => InkEditingModeViewModel.EnableTransparentCommand.Execute(null) }, + { HotKey.BlackboardBackground, () => InkEditingModeViewModel.EnableBlackboardCommand.Execute(null) }, + { HotKey.WhiteboardBackground, () => InkEditingModeViewModel.EnableWhiteboardCommand.Execute(null) }, + }; + BrushSettingsViewModel.PropertyChanged += (_, e) => OnBrushOrHighlightChanged(e.PropertyName); HighlighterViewModel.PropertyChanged += (_, e) => OnBrushOrHighlightChanged(e.PropertyName); } @@ -46,87 +79,41 @@ public PaletteStateOrchestratorViewModel( public DrawingAttributes InkDrawingAttributes => DrawingAttributesFactory.CreateDrawingAttributes(BrushSettingsViewModel.InkColor, BrushSettingsViewModel.SizeOfBrush, HighlighterViewModel.IsHighlighter); - public void HandleHotkey(HotKey hotKey) + public void HandleHotkey(HotKeiesPressed hotKeies) { - switch (hotKey) + var keiesMap = BuildKeyCombination(hotKeies); + var hotkey = _appSettings.HotKeysConfig.FirstOrDefault(x => x.Key == keiesMap); + if (hotkey is null) + { + return; + } + + if (!Enum.TryParse(hotkey.Value, out var hotKeyEnum)) { - case HotKey.Drawing: - DrawingViewModel.StartDrawingCommand.Execute(null); - break; - case HotKey.Erasing: - DrawingViewModel.StartErasingCommand.Execute(null); - break; - case HotKey.Trash: - StrokeHistoryViewModel.ClearCommand.Execute(null); - break; - case HotKey.Highlighter: - DrawingViewModel.ToggleHighlighterCommand.Execute(null); - break; - case HotKey.Release: - DrawingViewModel.ReleaseCommand.Execute(null); - break; - case HotKey.HideAll: - StrokeVisibilityViewModel.HideAllCommand.Execute(null); - break; - case HotKey.Undo: - StrokeHistoryViewModel.UndoCommand.Execute(null); - break; - case HotKey.Redo: - StrokeHistoryViewModel.RedoCommand.Execute(null); - break; - case HotKey.SizeOfBrush1X: - BrushSettingsViewModel.SetSizeOfBrushWithHotKeyCommand.Execute(BrushSize.X1); - break; - case HotKey.SizeOfBrush2X: - BrushSettingsViewModel.SetSizeOfBrushWithHotKeyCommand.Execute(BrushSize.X2); - break; - case HotKey.SizeOfBrush3X: - BrushSettingsViewModel.SetSizeOfBrushWithHotKeyCommand.Execute(BrushSize.X3); - break; - - case HotKey.Yellow: - BrushSettingsViewModel.SetInkColorWithHotKeyCommand.Execute(InkColor.Yellow); - break; - case HotKey.Purple: - BrushSettingsViewModel.SetInkColorWithHotKeyCommand.Execute(InkColor.Purple); - break; - case HotKey.Mint: - BrushSettingsViewModel.SetInkColorWithHotKeyCommand.Execute(InkColor.Mint); - break; - case HotKey.Coral: - BrushSettingsViewModel.SetInkColorWithHotKeyCommand.Execute(InkColor.Coral); - break; - case HotKey.Red: - BrushSettingsViewModel.SetInkColorWithHotKeyCommand.Execute(InkColor.Red); - break; - case HotKey.Cyan: - BrushSettingsViewModel.SetInkColorWithHotKeyCommand.Execute(InkColor.Cyan); - break; - case HotKey.White: - BrushSettingsViewModel.SetInkColorWithHotKeyCommand.Execute(InkColor.White); - break; - case HotKey.Orange: - BrushSettingsViewModel.SetInkColorWithHotKeyCommand.Execute(InkColor.Orange); - break; - case HotKey.Gray: - BrushSettingsViewModel.SetInkColorWithHotKeyCommand.Execute(InkColor.Gray); - break; - case HotKey.Black: - BrushSettingsViewModel.SetInkColorWithHotKeyCommand.Execute(InkColor.Black); - break; - - case HotKey.TransparentBackground: - InkEditingModeViewModel.EnableTransparentCommand.Execute(null); - break; - case HotKey.BlackboardBackground: - InkEditingModeViewModel.EnableBlackboardCommand.Execute(null); - break; - case HotKey.WhiteboardBackground: - InkEditingModeViewModel.EnableWhiteboardCommand.Execute(null); - break; - default: - break; + return; } + + if (_hotKeyCommandMap.TryGetValue(hotKeyEnum, out var command)) + { + command(); + } + } + + private static string BuildKeyCombination(HotKeiesPressed hotKeies) + { + var keiesMap = string.Empty; + if (hotKeies.CtrlPressed) + { + keiesMap += "CTRL + "; + } + + if (hotKeies.ShiftPressed) + { + keiesMap += "SHIFT + "; + } + + keiesMap += hotKeies.Key.ToString().ToUpper(); + return keiesMap; } private void OnBrushOrHighlightChanged(string propertyName) diff --git a/src/Riter/appsettings.json b/src/Riter/appsettings.json index 175e4a3..a752c17 100644 --- a/src/Riter/appsettings.json +++ b/src/Riter/appsettings.json @@ -2,172 +2,100 @@ "AppSettings": { "HotkeysConfig": [ { - "Key": "Drawing", - "Value": { - "Modifiers": "", - "Key": "D" - } + "Value": "Drawing", + "Key": "D" }, { - "Key": "Erasing", - "Value": { - "Modifiers": "", - "Key": "E" - } + "Value": "Erasing", + "Key": "E" }, { - "Key": "HideAll", - "Value": { - "Modifiers": "CTRL", - "Key": "H" - } + "Value": "HideAll", + "Key": "CTRL + H" }, { - "Key": "Trash", - "Value": { - "Modifiers": "CTRL", - "Key": "T" - } + "Value": "Trash", + "Key": "CTRL + T" }, { - "Key": "Undo", - "Value": { - "Modifiers": "", - "Key": "Z" - } + "Value": "Undo", + "Key": "Z" }, { - "Key": "Redo", - "Value": { - "Modifiers": "", - "Key": "X" - } + "Value": "Redo", + "Key": "X" }, { - "Key": "Highlighter", - "Value": { - "Modifiers": "", - "Key": "H" - } + "Value": "Highlighter", + "Key": "H" }, { - "Key": "Release", - "Value": { - "Modifiers": "", - "Key": "R" - } + "Value": "Release", + "Key": "R" }, { - "Key": "SizeOfBrush1X", - "Value": { - "Modifiers": "CTRL", - "Key": "1" - } + "Value": "SizeOfBrush1X", + "Key": "CTRL + D1" }, { - "Key": "SizeOfBrush2X", - "Value": { - "Modifiers": "CTRL", - "Key": "2" - } + "Value": "SizeOfBrush2X", + "Key": "CTRL + D2" }, { - "Key": "SizeOfBrush3X", - "Value": { - "Modifiers": "CTRL", - "Key": "3" - } + "Value": "SizeOfBrush3X", + "Key": "CTRL + D3" }, { - "Key": "TransparentBackground", - "Value": { - "Modifiers": "SHIFT", - "Key": "T" - } + "Value": "TransparentBackground", + "Key": "SHIFT + T" }, { - "Key": "WhiteboardBackground", - "Value": { - "Modifiers": "SHIFT", - "Key": "W" - } + "Value": "WhiteboardBackground", + "Key": "SHIFT + W" }, { - "Key": "BlackboardBackground", - "Value": { - "Modifiers": "SHIFT", - "Key": "B" - } + "Value": "BlackboardBackground", + "Key": "SHIFT + B" }, { - "Key": "Yellow", - "Value": { - "Modifiers": "ALT", - "Key": "1" - } + "Value": "Yellow", + "Key": "D1" }, { - "Key": "Purple", - "Value": { - "Modifiers": "ALT", - "Key": "2" - } + "Value": "Purple", + "Key": "D2" }, { - "Key": "Mint", - "Value": { - "Modifiers": "ALT", - "Key": "3" - } + "Value": "Mint", + "Key": "D3" }, { - "Key": "Coral", - "Value": { - "Modifiers": "ALT", - "Key": "4" - } + "Value": "Coral", + "Key": "D4" }, { - "Key": "Red", - "Value": { - "Modifiers": "ALT", - "Key": "5" - } + "Value": "Red", + "Key": "D5" }, { - "Key": "Cyan", - "Value": { - "Modifiers": "ALT", - "Key": "6" - } + "Value": "Cyan", + "Key": "D6" }, { - "Key": "White", - "Value": { - "Modifiers": "ALT", - "Key": "7" - } + "Value": "White", + "Key": "D7" }, { - "Key": "Orange", - "Value": { - "Modifiers": "ALT", - "Key": "8" - } + "Value": "Orange", + "Key": "D8" }, { - "Key": "Gray", - "Value": { - "Modifiers": "ALT", - "Key": "9" - } + "Value": "Gray", + "Key": "D9" }, { - "Key": "Black", - "Value": { - "Modifiers": "ALT", - "Key": "0" - } + "Value": "Black", + "Key": "D0" } ] }