HotkeySettings.cs
1 // Copyright (c) Microsoft Corporation 2 // The Microsoft Corporation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Globalization; 6 using System.Text; 7 using System.Text.Json.Serialization; 8 9 namespace Microsoft.CmdPal.UI.ViewModels.Settings; 10 11 public record HotkeySettings// : ICmdLineRepresentable 12 { 13 private const int VKTAB = 0x09; 14 15 public HotkeySettings() 16 { 17 Win = false; 18 Ctrl = false; 19 Alt = false; 20 Shift = false; 21 Code = 0; 22 } 23 24 /// <summary> 25 /// Initializes a new instance of the <see cref="HotkeySettings"/> class. 26 /// </summary> 27 /// <param name="win">Should Windows key be used</param> 28 /// <param name="ctrl">Should Ctrl key be used</param> 29 /// <param name="alt">Should Alt key be used</param> 30 /// <param name="shift">Should Shift key be used</param> 31 /// <param name="code">Go to https://learn.microsoft.com/windows/win32/inputdev/virtual-key-codes to see list of v-keys</param> 32 public HotkeySettings(bool win, bool ctrl, bool alt, bool shift, int code) 33 { 34 Win = win; 35 Ctrl = ctrl; 36 Alt = alt; 37 Shift = shift; 38 Code = code; 39 } 40 41 [JsonPropertyName("win")] 42 public bool Win { get; set; } 43 44 [JsonPropertyName("ctrl")] 45 public bool Ctrl { get; set; } 46 47 [JsonPropertyName("alt")] 48 public bool Alt { get; set; } 49 50 [JsonPropertyName("shift")] 51 public bool Shift { get; set; } 52 53 [JsonPropertyName("code")] 54 public int Code { get; set; } 55 56 // This is currently needed for FancyZones, we need to unify these two objects 57 // see src\common\settings_objects.h 58 [JsonPropertyName("key")] 59 public string Key { get; set; } = string.Empty; 60 61 public override string ToString() 62 { 63 var output = new StringBuilder(); 64 65 if (Win) 66 { 67 output.Append("Win + "); 68 } 69 70 if (Ctrl) 71 { 72 output.Append("Ctrl + "); 73 } 74 75 if (Alt) 76 { 77 output.Append("Alt + "); 78 } 79 80 if (Shift) 81 { 82 output.Append("Shift + "); 83 } 84 85 if (Code > 0) 86 { 87 var localKey = Helper.GetKeyName((uint)Code); 88 output.Append(localKey); 89 } 90 else if (output.Length >= 2) 91 { 92 output.Remove(output.Length - 2, 2); 93 } 94 95 return output.ToString(); 96 } 97 98 public List<object> GetKeysList() 99 { 100 var shortcutList = new List<object>(); 101 102 if (Win) 103 { 104 shortcutList.Add(92); // The Windows key or button. 105 } 106 107 if (Ctrl) 108 { 109 shortcutList.Add("Ctrl"); 110 } 111 112 if (Alt) 113 { 114 shortcutList.Add("Alt"); 115 } 116 117 if (Shift) 118 { 119 shortcutList.Add("Shift"); 120 121 // shortcutList.Add(16); // The Shift key or button. 122 } 123 124 if (Code > 0) 125 { 126 switch (Code) 127 { 128 // https://learn.microsoft.com/uwp/api/windows.system.virtualkey?view=winrt-20348 129 case 38: // The Up Arrow key or button. 130 case 40: // The Down Arrow key or button. 131 case 37: // The Left Arrow key or button. 132 case 39: // The Right Arrow key or button. 133 // case 8: // The Back key or button. 134 // case 13: // The Enter key or button. 135 shortcutList.Add(Code); 136 break; 137 default: 138 var localKey = Helper.GetKeyName((uint)Code); 139 shortcutList.Add(localKey); 140 break; 141 } 142 } 143 144 return shortcutList; 145 } 146 147 public bool IsValid() 148 { 149 return IsAccessibleShortcut() ? false : (Alt || Ctrl || Win || Shift) && Code != 0; 150 } 151 152 public bool IsEmpty() 153 { 154 return !Alt && !Ctrl && !Win && !Shift && Code == 0; 155 } 156 157 public bool IsAccessibleShortcut() 158 { 159 // Shift+Tab and Tab are accessible shortcuts 160 return (!Alt && !Ctrl && !Win && Shift && Code == VKTAB) 161 || (!Alt && !Ctrl && !Win && !Shift && Code == VKTAB); 162 } 163 164 public static bool TryParseFromCmd(string cmd, out object? result) 165 { 166 bool win = false, ctrl = false, alt = false, shift = false; 167 var code = 0; 168 169 var parts = cmd.Split('+'); 170 foreach (var part in parts) 171 { 172 switch (part.Trim().ToLower(CultureInfo.InvariantCulture)) 173 { 174 case "win": 175 win = true; 176 break; 177 case "ctrl": 178 ctrl = true; 179 break; 180 case "alt": 181 alt = true; 182 break; 183 case "shift": 184 shift = true; 185 break; 186 default: 187 if (!TryParseKeyCode(part, out code)) 188 { 189 result = null; 190 return false; 191 } 192 193 break; 194 } 195 } 196 197 result = new HotkeySettings(win, ctrl, alt, shift, code); 198 return true; 199 } 200 201 private static bool TryParseKeyCode(string key, out int keyCode) 202 { 203 // ASCII symbol 204 if (key.Length == 1 && char.IsLetterOrDigit(key[0])) 205 { 206 keyCode = char.ToUpper(key[0], CultureInfo.InvariantCulture); 207 return true; 208 } 209 210 // VK code 211 else if (key.Length == 4 && key.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) 212 { 213 return int.TryParse(key.AsSpan(2), NumberStyles.HexNumber, null, out keyCode); 214 } 215 216 // Alias 217 else 218 { 219 keyCode = (int)Helper.GetKeyValue(key); 220 return keyCode != 0; 221 } 222 } 223 224 public bool TryToCmdRepresentable(out string result) 225 { 226 result = ToString(); 227 result = result.Replace(" ", null); 228 return true; 229 } 230 }