8
8
9
9
namespace Files . App . Data . Commands
10
10
{
11
+ /// <summary>
12
+ /// Represents hot key.
13
+ /// </summary>
11
14
[ DebuggerDisplay ( "{Code}" ) ]
12
15
public readonly struct HotKey : IEquatable < HotKey >
13
16
{
14
- public static readonly FrozenDictionary < KeyModifiers , string > modifiers = new Dictionary < KeyModifiers , string > ( )
17
+ public static FrozenDictionary < KeyModifiers , string > LocalizedModifiers { get ; } = new Dictionary < KeyModifiers , string > ( )
15
18
{
16
19
[ KeyModifiers . Menu ] = GetKeyString ( "Menu" ) ,
17
20
[ KeyModifiers . Ctrl ] = GetKeyString ( "Control" ) ,
18
21
[ KeyModifiers . Shift ] = GetKeyString ( "Shift" ) ,
19
22
[ KeyModifiers . Win ] = GetKeyString ( "Windows" ) ,
20
23
} . ToFrozenDictionary ( ) ;
21
24
22
- public static readonly FrozenDictionary < Keys , string > keys = new Dictionary < Keys , string > ( )
25
+ public static FrozenDictionary < Keys , string > LocalizedKeys { get ; } = new Dictionary < Keys , string > ( )
23
26
{
24
27
[ Keys . Enter ] = GetKeyString ( "Enter" ) ,
25
28
[ Keys . Space ] = GetKeyString ( "Space" ) ,
@@ -153,29 +156,52 @@ namespace Files.App.Data.Commands
153
156
[ Keys . VolumeUp ] = GetKeyString ( "MediaVolumeUp" ) ,
154
157
} . ToFrozenDictionary ( ) ;
155
158
159
+ /// <summary>
160
+ /// Gets the none value.
161
+ /// </summary>
156
162
public static HotKey None { get ; } = new ( Keys . None , KeyModifiers . None ) ;
157
163
164
+ /// <summary>
165
+ /// Gets the value that indicates whether the hotkey is none.
166
+ /// </summary>
158
167
public bool IsNone => Key is Keys . None && Modifier is KeyModifiers . None ;
159
168
169
+ /// <summary>
170
+ /// Gets the value that indicates whether the key should be visible.
171
+ /// </summary>
172
+ /// <remarks>
173
+ /// This is always true for now.
174
+ /// </remarks>
160
175
public bool IsVisible { get ; init ; }
161
176
177
+ /// <summary>
178
+ /// Gets the key.
179
+ /// </summary>
162
180
public Keys Key { get ; }
181
+
182
+ /// <summary>
183
+ /// Gets the modifier.
184
+ /// </summary>
163
185
public KeyModifiers Modifier { get ; }
164
186
187
+ /// <summary>
188
+ /// Gets the raw humanized code of the hotkey.
189
+ /// </summary>
190
+ /// <remarks>
191
+ /// For example, this is "Ctrl+A" and "Ctrl+Menu+C"
192
+ /// </remarks>
165
193
public string Code
166
194
{
167
195
get
168
196
{
169
197
return ( Key , Modifier ) switch
170
198
{
171
199
( Keys . None , KeyModifiers . None ) => string . Empty ,
172
- ( Keys . None , _ ) => $ "{ GetVisibleCode ( IsVisible ) } { GetModifierCode ( Modifier ) } ",
173
- ( _, KeyModifiers . None ) => $ "{ GetVisibleCode ( IsVisible ) } { Key } ",
174
- _ => $ "{ GetVisibleCode ( IsVisible ) } { GetModifierCode ( Modifier ) } +{ Key } ",
200
+ ( Keys . None , _ ) => $ "{ GetModifierCode ( Modifier ) } ",
201
+ ( _, KeyModifiers . None ) => $ "{ Key } ",
202
+ _ => $ "{ GetModifierCode ( Modifier ) } +{ Key } ",
175
203
} ;
176
204
177
- static string GetVisibleCode ( bool isVisible ) => isVisible ? string . Empty : "!" ;
178
-
179
205
static string GetModifierCode ( KeyModifiers modifiers )
180
206
{
181
207
StringBuilder builder = new ( ) ;
@@ -193,6 +219,12 @@ static string GetModifierCode(KeyModifiers modifiers)
193
219
}
194
220
}
195
221
222
+ /// <summary>
223
+ /// Gets the humanized and localized label of the hotkey to shown in the UI.
224
+ /// </summary>
225
+ /// <remarks>
226
+ /// For example, this is "Ctrl+A" and "Ctrl+Alt+C"
227
+ /// </remarks>
196
228
public string Label
197
229
{
198
230
get
@@ -201,29 +233,34 @@ public string Label
201
233
{
202
234
( Keys . None , KeyModifiers . None ) => string . Empty ,
203
235
( Keys . None , _ ) => GetModifierCode ( Modifier ) ,
204
- ( _, KeyModifiers . None ) => keys [ Key ] ,
205
- _ => $ "{ GetModifierCode ( Modifier ) } +{ keys [ Key ] } ",
236
+ ( _, KeyModifiers . None ) => LocalizedKeys [ Key ] ,
237
+ _ => $ "{ GetModifierCode ( Modifier ) } +{ LocalizedKeys [ Key ] } ",
206
238
} ;
207
239
208
240
static string GetModifierCode ( KeyModifiers modifier )
209
241
{
210
242
StringBuilder builder = new ( ) ;
211
243
if ( modifier . HasFlag ( KeyModifiers . Menu ) )
212
- builder . Append ( $ "+{ modifiers [ KeyModifiers . Menu ] } ") ;
244
+ builder . Append ( $ "+{ LocalizedModifiers [ KeyModifiers . Menu ] } ") ;
213
245
if ( modifier . HasFlag ( KeyModifiers . Ctrl ) )
214
- builder . Append ( $ "+{ modifiers [ KeyModifiers . Ctrl ] } ") ;
246
+ builder . Append ( $ "+{ LocalizedModifiers [ KeyModifiers . Ctrl ] } ") ;
215
247
if ( modifier . HasFlag ( KeyModifiers . Shift ) )
216
- builder . Append ( $ "+{ modifiers [ KeyModifiers . Shift ] } ") ;
248
+ builder . Append ( $ "+{ LocalizedModifiers [ KeyModifiers . Shift ] } ") ;
217
249
if ( modifier . HasFlag ( KeyModifiers . Win ) )
218
- builder . Append ( $ "+{ modifiers [ KeyModifiers . Win ] } ") ;
250
+ builder . Append ( $ "+{ LocalizedModifiers [ KeyModifiers . Win ] } ") ;
219
251
builder . Remove ( 0 , 1 ) ;
220
252
return builder . ToString ( ) ;
221
253
}
222
254
}
223
255
}
224
256
225
- public HotKey ( Keys key , bool isVisible = true ) : this ( key , KeyModifiers . None , isVisible ) { }
226
- public HotKey ( Keys key , KeyModifiers modifier , bool isVisible = true )
257
+ /// <summary>
258
+ /// Initializes an instance of <see cref="HotKey"/>.
259
+ /// </summary>
260
+ /// <param name="key">A key</param>
261
+ /// <param name="modifier">A modifier</param>
262
+ /// <param name="isVisible">A value that indicates the hotkey should be available.</param>
263
+ public HotKey ( Keys key , KeyModifiers modifier = KeyModifiers . None , bool isVisible = true )
227
264
{
228
265
if ( ! Enum . IsDefined ( key ) || ! Enum . IsDefined ( modifier ) )
229
266
return ;
@@ -233,67 +270,65 @@ public HotKey(Keys key, KeyModifiers modifier, bool isVisible = true)
233
270
Modifier = modifier ;
234
271
}
235
272
236
- public void Deconstruct ( out Keys key , out KeyModifiers modifier )
237
- => ( key , modifier ) = ( Key , Modifier ) ;
238
- public void Deconstruct ( out Keys key , out KeyModifiers modifier , out bool isVisible )
239
- => ( key , modifier , isVisible ) = ( Key , Modifier , IsVisible ) ;
240
-
273
+ /// <summary>
274
+ /// Parses humanized hotkey code with separators.
275
+ /// </summary>
276
+ /// <param name="code">Humanized code to parse.</param>
277
+ /// <returns>Humanized code with a format <see cref="HotKey"/>.</returns>
241
278
public static HotKey Parse ( string code )
242
279
{
243
280
var key = Keys . None ;
244
281
var modifier = KeyModifiers . None ;
245
282
bool isVisible = true ;
246
283
247
284
code = code . Trim ( ) ;
248
- if ( code . StartsWith ( '!' ) )
249
- {
250
- isVisible = false ;
251
- code = code . Remove ( 0 , 1 ) ;
252
- }
253
-
254
285
var parts = code . Split ( '+' ) . Select ( part => part . Trim ( ) ) ;
286
+
255
287
foreach ( var part in parts )
256
288
{
257
- if ( Enum . TryParse ( part , true , out Keys partKey ) )
258
- key = partKey ;
259
- if ( Enum . TryParse ( part , true , out KeyModifiers partModifier ) )
260
- modifier |= partModifier ;
289
+ key |= LocalizedKeys . FirstOrDefault ( x => x . Value == part ) . Key ;
290
+ modifier |= LocalizedModifiers . FirstOrDefault ( x => x . Value == part ) . Key ;
261
291
}
292
+
262
293
return new ( key , modifier , isVisible ) ;
263
294
}
264
295
265
- public HotKeyCollection AsCollection ( ) => new ( this ) ;
296
+ /// <summary>
297
+ /// Converts this <see cref="HotKey"/> instance into a <see cref="HotKeyCollection"/> instance.
298
+ /// </summary>
299
+ /// <returns></returns>
300
+ public HotKeyCollection AsCollection ( )
301
+ {
302
+ return new ( this ) ;
303
+ }
266
304
267
- public static implicit operator string ( HotKey hotKey ) => hotKey . Label ;
305
+ // Operator overloads
268
306
307
+ public static implicit operator string ( HotKey hotKey ) => hotKey . Label ;
269
308
public static bool operator == ( HotKey a , HotKey b ) => a . Equals ( b ) ;
270
309
public static bool operator != ( HotKey a , HotKey b ) => ! a . Equals ( b ) ;
271
310
272
- public override string ToString ( ) => Label ;
311
+ // Default methods
273
312
313
+ public override string ToString ( ) => Label ;
274
314
public override int GetHashCode ( ) => ( Key , Modifier , IsVisible ) . GetHashCode ( ) ;
275
315
public override bool Equals ( object ? other ) => other is HotKey hotKey && Equals ( hotKey ) ;
276
316
public bool Equals ( HotKey other ) => ( other . Key , other . Modifier , other . IsVisible ) . Equals ( ( Key , Modifier , IsVisible ) ) ;
277
317
278
- private static string GetKeyString ( string key ) => $ "Key/{ key } ". GetLocalizedResource ( ) ;
318
+ // Private methods
319
+
320
+ private static string GetKeyString ( string key )
321
+ {
322
+ return $ "Key/{ key } ". GetLocalizedResource ( ) ;
323
+ }
279
324
280
325
private static string GetKeyCharacter ( Forms . Keys key )
281
326
{
282
327
var buffer = new StringBuilder ( 256 ) ;
283
328
var state = new byte [ 256 ] ;
284
- _ = ToUnicode ( ( uint ) key , 0 , state , buffer , 256 , 0 ) ;
329
+ _ = Win32PInvoke . ToUnicode ( ( uint ) key , 0 , state , buffer , 256 , 0 ) ;
330
+
285
331
return buffer . ToString ( ) ;
286
332
}
287
-
288
- [ DllImport ( "user32.dll" ) ]
289
- private static extern int ToUnicode
290
- (
291
- uint virtualKeyCode ,
292
- uint scanCode ,
293
- byte [ ] keyboardState ,
294
- [ Out , MarshalAs ( UnmanagedType . LPWStr , SizeConst = 64 ) ] StringBuilder receivingBuffer ,
295
- int bufferSize ,
296
- uint flags
297
- ) ;
298
333
}
299
334
}
0 commit comments