Skip to content

Commit 8d6680e

Browse files
authored
Localized Hotkeys (#11860)
1 parent 5348c03 commit 8d6680e

File tree

3 files changed

+386
-104
lines changed

3 files changed

+386
-104
lines changed

src/Files.App/Commands/HotKey.cs

Lines changed: 2 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,10 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.Collections.Immutable;
4-
using System.Linq;
5-
using System.Text;
62
using Windows.System;
73

84
namespace Files.App.Commands
95
{
106
public readonly struct HotKey : IEquatable<HotKey>
117
{
12-
private static IImmutableDictionary<VirtualKey, string> keys = new Dictionary<VirtualKey, string>
13-
{
14-
[VirtualKey.Number0] = "0",
15-
[VirtualKey.Number1] = "1",
16-
[VirtualKey.Number2] = "2",
17-
[VirtualKey.Number3] = "3",
18-
[VirtualKey.Number4] = "4",
19-
[VirtualKey.Number5] = "5",
20-
[VirtualKey.Number6] = "6",
21-
[VirtualKey.Number7] = "7",
22-
[VirtualKey.Number8] = "8",
23-
[VirtualKey.Number9] = "9",
24-
[VirtualKey.NumberPad0] = "Pad0",
25-
[VirtualKey.NumberPad1] = "Pad1",
26-
[VirtualKey.NumberPad2] = "Pad2",
27-
[VirtualKey.NumberPad3] = "Pad3",
28-
[VirtualKey.NumberPad4] = "Pad4",
29-
[VirtualKey.NumberPad5] = "Pad5",
30-
[VirtualKey.NumberPad6] = "Pad6",
31-
[VirtualKey.NumberPad7] = "Pad7",
32-
[VirtualKey.NumberPad8] = "Pad8",
33-
[VirtualKey.NumberPad9] = "Pad9",
34-
[VirtualKey.Add] = "+",
35-
[VirtualKey.Subtract] = "-",
36-
[VirtualKey.Delete] = "Del",
37-
[(VirtualKey)192] = "`",
38-
}.ToImmutableDictionary();
39-
408
public static HotKey None { get; } = new(VirtualKey.None, VirtualKeyModifiers.None);
419

4210
public bool IsNone => Key is VirtualKey.None;
@@ -47,7 +15,7 @@ namespace Files.App.Commands
4715
public HotKey(VirtualKey key) : this(key, VirtualKeyModifiers.None) { }
4816
public HotKey(VirtualKey key, VirtualKeyModifiers modifiers)
4917
{
50-
if (key is VirtualKey.None)
18+
if (!key.IsValid())
5119
return;
5220

5321
if (IsModifier(key))
@@ -66,78 +34,12 @@ or VirtualKey.Shift or VirtualKey.LeftShift or VirtualKey.RightShift
6634
public void Deconstruct(out VirtualKey key, out VirtualKeyModifiers modifiers)
6735
=> (key, modifiers) = (Key, Modifiers);
6836

69-
public static explicit operator HotKey(string hotKey) => Parse(hotKey);
7037
public static implicit operator string(HotKey hotKey) => hotKey.ToString();
7138

7239
public static bool operator ==(HotKey a, HotKey b) => a.Equals(b);
7340
public static bool operator !=(HotKey a, HotKey b) => !a.Equals(b);
7441

75-
public static HotKey Parse(string hotKey)
76-
{
77-
var key = VirtualKey.None;
78-
var modifiers = VirtualKeyModifiers.None;
79-
80-
var parts = hotKey.Split('+').Select(item => item.Trim().ToLower());
81-
foreach (string part in parts)
82-
{
83-
var m = ToModifiers(part);
84-
if (m is not VirtualKeyModifiers.None)
85-
{
86-
modifiers |= m;
87-
continue;
88-
}
89-
90-
if (key is VirtualKey.None)
91-
{
92-
var k = ToKey(part);
93-
if (k is not VirtualKey.None)
94-
{
95-
key = k;
96-
continue;
97-
}
98-
}
99-
100-
throw new FormatException($"{hotKey} is not a valid hot key");
101-
}
102-
103-
return new(key, modifiers);
104-
105-
static VirtualKeyModifiers ToModifiers(string modifiers) => modifiers switch
106-
{
107-
"alt" or "menu " => VirtualKeyModifiers.Menu,
108-
"ctrl" or "control" => VirtualKeyModifiers.Control,
109-
"shift" => VirtualKeyModifiers.Shift,
110-
_ => VirtualKeyModifiers.None,
111-
};
112-
113-
static VirtualKey ToKey(string part)
114-
{
115-
if (part is "alt" or "menu" or "ctrl" or "control" or "shift" or "windows")
116-
return VirtualKey.None;
117-
118-
if (keys.Values.Contains(part))
119-
return keys.First(k => k.Value == part).Key;
120-
121-
return Enum.TryParse(part, true, out VirtualKey key) ? key : VirtualKey.None;
122-
}
123-
}
124-
125-
public override string ToString()
126-
{
127-
StringBuilder builder = new();
128-
if (Modifiers.HasFlag(VirtualKeyModifiers.Menu))
129-
builder.Append("Alt+");
130-
if (Modifiers.HasFlag(VirtualKeyModifiers.Control))
131-
builder.Append("Ctrl+");
132-
if (Modifiers.HasFlag(VirtualKeyModifiers.Shift))
133-
builder.Append("Shift+");
134-
if (Modifiers.HasFlag(VirtualKeyModifiers.Windows))
135-
builder.Append("Win+");
136-
builder.Append(ToString(Key));
137-
return builder.ToString();
138-
139-
static string ToString(VirtualKey key) => keys.ContainsKey(key) ? keys[key] : key.ToString();
140-
}
42+
public override string ToString() => HotKeyHelpers.ToString(this);
14143

14244
public override int GetHashCode() => (Key, Modifiers).GetHashCode();
14345
public override bool Equals(object? other) => other is HotKey hotKey && Equals(hotKey);

src/Files.App/Commands/HotKeyHelpers.cs

Lines changed: 200 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,159 @@
1-
using Microsoft.UI.Input;
1+
using Files.App.Extensions;
2+
using Microsoft.UI.Input;
23
using System.Collections.Generic;
34
using System.Collections.Immutable;
5+
using System.Runtime.InteropServices;
6+
using System.Text;
47
using Windows.System;
58
using Windows.UI.Core;
9+
using Forms = System.Windows.Forms;
610

711
namespace Files.App.Commands
812
{
913
internal static class HotKeyHelpers
1014
{
15+
private static readonly IImmutableDictionary<VirtualKeyModifiers, string> modifiers = new Dictionary<VirtualKeyModifiers, string>()
16+
{
17+
[VirtualKeyModifiers.Menu] = "Menu".GetString(),
18+
[VirtualKeyModifiers.Control] = "Control".GetString(),
19+
[VirtualKeyModifiers.Shift] = "Shift".GetString(),
20+
[VirtualKeyModifiers.Windows] = "Windows".GetString(),
21+
}.ToImmutableDictionary();
22+
23+
private static readonly IImmutableDictionary<VirtualKey, string> keys = new Dictionary<VirtualKey, string>()
24+
{
25+
[VirtualKey.Enter] = "Enter".GetString(),
26+
[VirtualKey.Space] = "Space".GetString(),
27+
[VirtualKey.Escape] = "Escape".GetString(),
28+
[VirtualKey.Back] = "Back".GetString(),
29+
[VirtualKey.Tab] = "Tab".GetString(),
30+
[VirtualKey.Insert] = "Insert".GetString(),
31+
[VirtualKey.Delete] = "Delete".GetString(),
32+
[VirtualKey.Left] = "Left".GetString(),
33+
[VirtualKey.Right] = "Right".GetString(),
34+
[VirtualKey.Down] = "Down".GetString(),
35+
[VirtualKey.Up] = "Up".GetString(),
36+
[VirtualKey.Home] = "Home".GetString(),
37+
[VirtualKey.End] = "End".GetString(),
38+
[VirtualKey.PageDown] = "PageDown".GetString(),
39+
[VirtualKey.PageUp] = "PageUp".GetString(),
40+
[VirtualKey.Separator] = "Separator".GetString(),
41+
[VirtualKey.Pause] = "Pause".GetString(),
42+
[VirtualKey.Sleep] = "Sleep".GetString(),
43+
[VirtualKey.Clear] = "Clear".GetString(),
44+
[VirtualKey.Print] = "Print".GetString(),
45+
[VirtualKey.Help] = "Help".GetString(),
46+
[VirtualKey.XButton1] = "Mouse4".GetString(),
47+
[VirtualKey.XButton2] = "Mouse5".GetString(),
48+
[VirtualKey.F1] = "F1",
49+
[VirtualKey.F2] = "F2",
50+
[VirtualKey.F3] = "F3",
51+
[VirtualKey.F4] = "F4",
52+
[VirtualKey.F5] = "F5",
53+
[VirtualKey.F6] = "F6",
54+
[VirtualKey.F7] = "F7",
55+
[VirtualKey.F8] = "F8",
56+
[VirtualKey.F9] = "F9",
57+
[VirtualKey.F10] = "F10",
58+
[VirtualKey.F11] = "F11",
59+
[VirtualKey.F12] = "F12",
60+
[VirtualKey.F13] = "F13",
61+
[VirtualKey.F14] = "F14",
62+
[VirtualKey.F15] = "F15",
63+
[VirtualKey.F16] = "F16",
64+
[VirtualKey.F17] = "F17",
65+
[VirtualKey.F18] = "F18",
66+
[VirtualKey.F19] = "F19",
67+
[VirtualKey.F20] = "F20",
68+
[VirtualKey.F21] = "F21",
69+
[VirtualKey.F22] = "F22",
70+
[VirtualKey.F23] = "F23",
71+
[VirtualKey.F24] = "F24",
72+
[VirtualKey.Number0] = "0",
73+
[VirtualKey.Number1] = "1",
74+
[VirtualKey.Number2] = "2",
75+
[VirtualKey.Number3] = "3",
76+
[VirtualKey.Number4] = "4",
77+
[VirtualKey.Number5] = "5",
78+
[VirtualKey.Number6] = "6",
79+
[VirtualKey.Number7] = "7",
80+
[VirtualKey.Number8] = "8",
81+
[VirtualKey.Number9] = "9",
82+
[VirtualKey.NumberPad0] = "Pad0".GetString(),
83+
[VirtualKey.NumberPad1] = "Pad1".GetString(),
84+
[VirtualKey.NumberPad2] = "Pad2".GetString(),
85+
[VirtualKey.NumberPad3] = "Pad3".GetString(),
86+
[VirtualKey.NumberPad4] = "Pad4".GetString(),
87+
[VirtualKey.NumberPad5] = "Pad5".GetString(),
88+
[VirtualKey.NumberPad6] = "Pad6".GetString(),
89+
[VirtualKey.NumberPad7] = "Pad7".GetString(),
90+
[VirtualKey.NumberPad8] = "Pad8".GetString(),
91+
[VirtualKey.NumberPad9] = "Pad9".GetString(),
92+
[VirtualKey.A] = "A",
93+
[VirtualKey.B] = "B",
94+
[VirtualKey.C] = "C",
95+
[VirtualKey.D] = "D",
96+
[VirtualKey.E] = "E",
97+
[VirtualKey.F] = "F",
98+
[VirtualKey.G] = "G",
99+
[VirtualKey.H] = "H",
100+
[VirtualKey.I] = "I",
101+
[VirtualKey.J] = "J",
102+
[VirtualKey.K] = "K",
103+
[VirtualKey.L] = "L",
104+
[VirtualKey.M] = "M",
105+
[VirtualKey.N] = "N",
106+
[VirtualKey.O] = "O",
107+
[VirtualKey.P] = "P",
108+
[VirtualKey.Q] = "Q",
109+
[VirtualKey.R] = "R",
110+
[VirtualKey.S] = "S",
111+
[VirtualKey.T] = "T",
112+
[VirtualKey.U] = "U",
113+
[VirtualKey.V] = "V",
114+
[VirtualKey.W] = "W",
115+
[VirtualKey.X] = "X",
116+
[VirtualKey.Y] = "Y",
117+
[VirtualKey.Z] = "Z",
118+
[VirtualKey.Add] = "+",
119+
[VirtualKey.Subtract] = "-",
120+
[VirtualKey.Multiply] = "*",
121+
[VirtualKey.Divide] = "/",
122+
[(VirtualKey)186] = GetCharacter(Forms.Keys.Oem1),
123+
[(VirtualKey)187] = GetCharacter(Forms.Keys.Oemplus),
124+
[(VirtualKey)188] = GetCharacter(Forms.Keys.Oemcomma),
125+
[(VirtualKey)189] = GetCharacter(Forms.Keys.OemMinus),
126+
[(VirtualKey)190] = GetCharacter(Forms.Keys.OemPeriod),
127+
[(VirtualKey)191] = GetCharacter(Forms.Keys.Oem2),
128+
[(VirtualKey)192] = GetCharacter(Forms.Keys.Oem3),
129+
[(VirtualKey)219] = GetCharacter(Forms.Keys.Oem4),
130+
[(VirtualKey)220] = GetCharacter(Forms.Keys.Oem5),
131+
[(VirtualKey)221] = GetCharacter(Forms.Keys.Oem6),
132+
[(VirtualKey)222] = GetCharacter(Forms.Keys.Oem7),
133+
[(VirtualKey)223] = GetCharacter(Forms.Keys.Oem8),
134+
[(VirtualKey)226] = GetCharacter(Forms.Keys.Oem102),
135+
[(VirtualKey)254] = GetCharacter(Forms.Keys.OemClear),
136+
[VirtualKey.Application] = "Application".GetString(),
137+
[(VirtualKey)182] = "Application1".GetString(),
138+
[(VirtualKey)183] = "Application2".GetString(),
139+
[(VirtualKey)180] = "Mail".GetString(),
140+
[VirtualKey.GoHome] = "BrowserGoHome".GetString(),
141+
[VirtualKey.GoBack] = "BrowserGoBack".GetString(),
142+
[VirtualKey.GoForward] = "BrowserGoForward".GetString(),
143+
[VirtualKey.Refresh] = "BrowserRefresh".GetString(),
144+
[VirtualKey.Stop] = "BrowserStop".GetString(),
145+
[VirtualKey.Search] = "BrowserSearch".GetString(),
146+
[VirtualKey.Favorites] = "BrowserFavorites".GetString(),
147+
[(VirtualKey)179] = "MediaPlayPause".GetString(),
148+
[(VirtualKey)178] = "MediaStop".GetString(),
149+
[(VirtualKey)177] = "MediaPreviousTrack".GetString(),
150+
[(VirtualKey)176] = "MediaNextTrack".GetString(),
151+
[(VirtualKey)181] = "MediaSelect".GetString(),
152+
[(VirtualKey)173] = "MediaMute".GetString(),
153+
[(VirtualKey)174] = "MediaVolumeDown".GetString(),
154+
[(VirtualKey)175] = "MediaVolumeUp".GetString(),
155+
}.ToImmutableDictionary();
156+
11157
private static readonly ImmutableArray<VirtualKey> globalKeys = new List<VirtualKey>
12158
{
13159
VirtualKey.GoHome,
@@ -55,6 +201,9 @@ internal static class HotKeyHelpers
55201
new HotKey(VirtualKey.Z, VirtualKeyModifiers.Control), // Cancel
56202
}.ToImmutableArray();
57203

204+
public static bool IsValid(this VirtualKeyModifiers modifier) => modifiers.ContainsKey(modifier);
205+
public static bool IsValid(this VirtualKey key) => keys.ContainsKey(key);
206+
58207
public static bool IsGlobalKey(this VirtualKey key) => globalKeys.Contains(key);
59208
public static bool IsTextBoxHotKey(this HotKey hotKey) => textBoxHotKeys.Contains(hotKey);
60209

@@ -82,9 +231,56 @@ public static VirtualKeyModifiers GetCurrentKeyModifiers()
82231
return modifiers;
83232

84233
static bool IsPressed(VirtualKey key)
85-
{
86-
return InputKeyboardSource.GetKeyStateForCurrentThread(key).HasFlag(CoreVirtualKeyStates.Down);
87-
}
234+
=> InputKeyboardSource.GetKeyStateForCurrentThread(key).HasFlag(CoreVirtualKeyStates.Down);
88235
}
236+
237+
public static string ToString(HotKey hotKey)
238+
{
239+
return hotKey.Modifiers is VirtualKeyModifiers.None
240+
? ToString(hotKey.Key)
241+
: $"{ToString(hotKey.Modifiers)}+{ToString(hotKey.Key)}";
242+
}
243+
public static string ToString(VirtualKeyModifiers modifier)
244+
{
245+
StringBuilder builder = new();
246+
if (modifier.HasFlag(VirtualKeyModifiers.Menu))
247+
builder.Append($"+{modifiers[VirtualKeyModifiers.Menu]}");
248+
if (modifier.HasFlag(VirtualKeyModifiers.Control))
249+
builder.Append($"+{modifiers[VirtualKeyModifiers.Control]}");
250+
if (modifier.HasFlag(VirtualKeyModifiers.Shift))
251+
builder.Append($"+{modifiers[VirtualKeyModifiers.Shift]}");
252+
if (modifier.HasFlag(VirtualKeyModifiers.Windows))
253+
builder.Append($"+{modifiers[VirtualKeyModifiers.Windows]}");
254+
if (modifier is not VirtualKeyModifiers.None)
255+
builder.Remove(0, 1);
256+
return builder.ToString();
257+
}
258+
public static string ToString(VirtualKey key)
259+
{
260+
return keys.TryGetValue(key, out string? label) ? label : string.Empty;
261+
}
262+
263+
private static string GetString(this string key) => $"Key/{key}".GetLocalizedResource();
264+
265+
private static string GetCharacter(Forms.Keys key)
266+
{
267+
var buffer = new StringBuilder(256);
268+
var state = new byte[256];
269+
_ = ToUnicode((uint)key, 0, state, buffer, 256, 0);
270+
return buffer.ToString();
271+
}
272+
273+
[DllImport("user32.dll")]
274+
private static extern int ToUnicode
275+
(
276+
uint virtualKeyCode,
277+
uint scanCode,
278+
byte[] keyboardState,
279+
[Out, MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)] StringBuilder receivingBuffer,
280+
int bufferSize,
281+
uint flags
282+
);
283+
284+
89285
}
90286
}

0 commit comments

Comments
 (0)