widgem 0.0.1

A lightweight cross-platform desktop widget toolkit
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
use winit::keyboard::{KeyCode, NamedKey};

pub fn parse_key(text: &str) -> Option<NamedKey> {
    let key = match text {
        "alt" => NamedKey::Alt,
        "altgraph" => NamedKey::AltGraph,
        "capslock" => NamedKey::CapsLock,
        "control" | "ctrl" => NamedKey::Control,
        "fn" => NamedKey::Fn,
        "fnlock" => NamedKey::FnLock,
        "numlock" => NamedKey::NumLock,
        "scrolllock" => NamedKey::ScrollLock,
        "shift" => NamedKey::Shift,
        "symbol" => NamedKey::Symbol,
        "symbollock" => NamedKey::SymbolLock,
        "meta" => NamedKey::Meta,
        "hyper" => NamedKey::Hyper,
        "super" => NamedKey::Super,
        "enter" => NamedKey::Enter,
        "tab" => NamedKey::Tab,
        "space" => NamedKey::Space,
        "arrowdown" | "down" => NamedKey::ArrowDown,
        "arrowleft" | "left" => NamedKey::ArrowLeft,
        "arrowright" | "right" => NamedKey::ArrowRight,
        "arrowup" | "up" => NamedKey::ArrowUp,
        "end" => NamedKey::End,
        "home" => NamedKey::Home,
        "pagedown" | "pgdown" => NamedKey::PageDown,
        "pageup" | "pgup" => NamedKey::PageUp,
        "backspace" => NamedKey::Backspace,
        "clear" => NamedKey::Clear,
        "copy" => NamedKey::Copy,
        "crsel" => NamedKey::CrSel,
        "cut" => NamedKey::Cut,
        "delete" => NamedKey::Delete,
        "eraseeof" => NamedKey::EraseEof,
        "exsel" => NamedKey::ExSel,
        "insert" => NamedKey::Insert,
        "paste" => NamedKey::Paste,
        "redo" => NamedKey::Redo,
        "undo" => NamedKey::Undo,
        "accept" => NamedKey::Accept,
        "again" => NamedKey::Again,
        "attn" => NamedKey::Attn,
        "cancel" => NamedKey::Cancel,
        "contextmenu" => NamedKey::ContextMenu,
        "escape" => NamedKey::Escape,
        "execute" => NamedKey::Execute,
        "find" => NamedKey::Find,
        "help" => NamedKey::Help,
        "pause" => NamedKey::Pause,
        "play" => NamedKey::Play,
        "props" => NamedKey::Props,
        "select" => NamedKey::Select,
        "zoomin" => NamedKey::ZoomIn,
        "zoomout" => NamedKey::ZoomOut,
        "brightnessdown" => NamedKey::BrightnessDown,
        "brightnessup" => NamedKey::BrightnessUp,
        "eject" => NamedKey::Eject,
        "logoff" => NamedKey::LogOff,
        "power" => NamedKey::Power,
        "poweroff" => NamedKey::PowerOff,
        "printscreen" => NamedKey::PrintScreen,
        "hibernate" => NamedKey::Hibernate,
        "standby" => NamedKey::Standby,
        "wakeup" => NamedKey::WakeUp,
        "allcandidates" => NamedKey::AllCandidates,
        "alphanumeric" => NamedKey::Alphanumeric,
        "codeinput" => NamedKey::CodeInput,
        "compose" => NamedKey::Compose,
        "convert" => NamedKey::Convert,
        "finalmode" => NamedKey::FinalMode,
        "groupfirst" => NamedKey::GroupFirst,
        "grouplast" => NamedKey::GroupLast,
        "groupnext" => NamedKey::GroupNext,
        "groupprevious" => NamedKey::GroupPrevious,
        "modechange" => NamedKey::ModeChange,
        "nextcandidate" => NamedKey::NextCandidate,
        "nonconvert" => NamedKey::NonConvert,
        "previouscandidate" => NamedKey::PreviousCandidate,
        "process" => NamedKey::Process,
        "singlecandidate" => NamedKey::SingleCandidate,
        "hangulmode" => NamedKey::HangulMode,
        "hanjamode" => NamedKey::HanjaMode,
        "junjamode" => NamedKey::JunjaMode,
        "eisu" => NamedKey::Eisu,
        "hankaku" => NamedKey::Hankaku,
        "hiragana" => NamedKey::Hiragana,
        "hiraganakatakana" => NamedKey::HiraganaKatakana,
        "kanamode" => NamedKey::KanaMode,
        "kanjimode" => NamedKey::KanjiMode,
        "katakana" => NamedKey::Katakana,
        "romaji" => NamedKey::Romaji,
        "zenkaku" => NamedKey::Zenkaku,
        "zenkakuhankaku" => NamedKey::ZenkakuHankaku,
        "soft1" => NamedKey::Soft1,
        "soft2" => NamedKey::Soft2,
        "soft3" => NamedKey::Soft3,
        "soft4" => NamedKey::Soft4,
        "channeldown" => NamedKey::ChannelDown,
        "channelup" => NamedKey::ChannelUp,
        "close" => NamedKey::Close,
        "mailforward" => NamedKey::MailForward,
        "mailreply" => NamedKey::MailReply,
        "mailsend" => NamedKey::MailSend,
        "mediaclose" => NamedKey::MediaClose,
        "mediafastforward" => NamedKey::MediaFastForward,
        "mediapause" => NamedKey::MediaPause,
        "mediaplay" => NamedKey::MediaPlay,
        "mediaplaypause" => NamedKey::MediaPlayPause,
        "mediarecord" => NamedKey::MediaRecord,
        "mediarewind" => NamedKey::MediaRewind,
        "mediastop" => NamedKey::MediaStop,
        "mediatracknext" => NamedKey::MediaTrackNext,
        "mediatrackprevious" => NamedKey::MediaTrackPrevious,
        "new" => NamedKey::New,
        "open" => NamedKey::Open,
        "print" => NamedKey::Print,
        "save" => NamedKey::Save,
        "spellcheck" => NamedKey::SpellCheck,
        "key11" => NamedKey::Key11,
        "key12" => NamedKey::Key12,
        "audiobalanceleft" => NamedKey::AudioBalanceLeft,
        "audiobalanceright" => NamedKey::AudioBalanceRight,
        "audiobassboostdown" => NamedKey::AudioBassBoostDown,
        "audiobassboosttoggle" => NamedKey::AudioBassBoostToggle,
        "audiobassboostup" => NamedKey::AudioBassBoostUp,
        "audiofaderfront" => NamedKey::AudioFaderFront,
        "audiofaderrear" => NamedKey::AudioFaderRear,
        "audiosurroundmodenext" => NamedKey::AudioSurroundModeNext,
        "audiotrebledown" => NamedKey::AudioTrebleDown,
        "audiotrebleup" => NamedKey::AudioTrebleUp,
        "audiovolumedown" => NamedKey::AudioVolumeDown,
        "audiovolumeup" => NamedKey::AudioVolumeUp,
        "audiovolumemute" => NamedKey::AudioVolumeMute,
        "microphonetoggle" => NamedKey::MicrophoneToggle,
        "microphonevolumedown" => NamedKey::MicrophoneVolumeDown,
        "microphonevolumeup" => NamedKey::MicrophoneVolumeUp,
        "microphonevolumemute" => NamedKey::MicrophoneVolumeMute,
        "speechcorrectionlist" => NamedKey::SpeechCorrectionList,
        "speechinputtoggle" => NamedKey::SpeechInputToggle,
        "launchapplication1" => NamedKey::LaunchApplication1,
        "launchapplication2" => NamedKey::LaunchApplication2,
        "launchcalendar" => NamedKey::LaunchCalendar,
        "launchcontacts" => NamedKey::LaunchContacts,
        "launchmail" => NamedKey::LaunchMail,
        "launchmediaplayer" => NamedKey::LaunchMediaPlayer,
        "launchmusicplayer" => NamedKey::LaunchMusicPlayer,
        "launchphone" => NamedKey::LaunchPhone,
        "launchscreensaver" => NamedKey::LaunchScreenSaver,
        "launchspreadsheet" => NamedKey::LaunchSpreadsheet,
        "launchwebbrowser" => NamedKey::LaunchWebBrowser,
        "launchwebcam" => NamedKey::LaunchWebCam,
        "launchwordprocessor" => NamedKey::LaunchWordProcessor,
        "browserback" => NamedKey::BrowserBack,
        "browserfavorites" => NamedKey::BrowserFavorites,
        "browserforward" => NamedKey::BrowserForward,
        "browserhome" => NamedKey::BrowserHome,
        "browserrefresh" => NamedKey::BrowserRefresh,
        "browsersearch" => NamedKey::BrowserSearch,
        "browserstop" => NamedKey::BrowserStop,
        "appswitch" => NamedKey::AppSwitch,
        "call" => NamedKey::Call,
        "camera" => NamedKey::Camera,
        "camerafocus" => NamedKey::CameraFocus,
        "endcall" => NamedKey::EndCall,
        "goback" => NamedKey::GoBack,
        "gohome" => NamedKey::GoHome,
        "headsethook" => NamedKey::HeadsetHook,
        "lastnumberredial" => NamedKey::LastNumberRedial,
        "notification" => NamedKey::Notification,
        "mannermode" => NamedKey::MannerMode,
        "voicedial" => NamedKey::VoiceDial,
        "tv" => NamedKey::TV,
        "tv3dmode" => NamedKey::TV3DMode,
        "tvantennacable" => NamedKey::TVAntennaCable,
        "tvaudiodescription" => NamedKey::TVAudioDescription,
        "tvaudiodescriptionmixdown" => NamedKey::TVAudioDescriptionMixDown,
        "tvaudiodescriptionmixup" => NamedKey::TVAudioDescriptionMixUp,
        "tvcontentsmenu" => NamedKey::TVContentsMenu,
        "tvdataservice" => NamedKey::TVDataService,
        "tvinput" => NamedKey::TVInput,
        "tvinputcomponent1" => NamedKey::TVInputComponent1,
        "tvinputcomponent2" => NamedKey::TVInputComponent2,
        "tvinputcomposite1" => NamedKey::TVInputComposite1,
        "tvinputcomposite2" => NamedKey::TVInputComposite2,
        "tvinputhdmi1" => NamedKey::TVInputHDMI1,
        "tvinputhdmi2" => NamedKey::TVInputHDMI2,
        "tvinputhdmi3" => NamedKey::TVInputHDMI3,
        "tvinputhdmi4" => NamedKey::TVInputHDMI4,
        "tvinputvga1" => NamedKey::TVInputVGA1,
        "tvmediacontext" => NamedKey::TVMediaContext,
        "tvnetwork" => NamedKey::TVNetwork,
        "tvnumberentry" => NamedKey::TVNumberEntry,
        "tvpower" => NamedKey::TVPower,
        "tvradioservice" => NamedKey::TVRadioService,
        "tvsatellite" => NamedKey::TVSatellite,
        "tvsatellitebs" => NamedKey::TVSatelliteBS,
        "tvsatellitecs" => NamedKey::TVSatelliteCS,
        "tvsatellitetoggle" => NamedKey::TVSatelliteToggle,
        "tvterrestrialanalog" => NamedKey::TVTerrestrialAnalog,
        "tvterrestrialdigital" => NamedKey::TVTerrestrialDigital,
        "tvtimer" => NamedKey::TVTimer,
        "avrinput" => NamedKey::AVRInput,
        "avrpower" => NamedKey::AVRPower,
        "colorf0red" => NamedKey::ColorF0Red,
        "colorf1green" => NamedKey::ColorF1Green,
        "colorf2yellow" => NamedKey::ColorF2Yellow,
        "colorf3blue" => NamedKey::ColorF3Blue,
        "colorf4grey" => NamedKey::ColorF4Grey,
        "colorf5brown" => NamedKey::ColorF5Brown,
        "closedcaptiontoggle" => NamedKey::ClosedCaptionToggle,
        "dimmer" => NamedKey::Dimmer,
        "displayswap" => NamedKey::DisplaySwap,
        "dvr" => NamedKey::DVR,
        "exit" => NamedKey::Exit,
        "favoriteclear0" => NamedKey::FavoriteClear0,
        "favoriteclear1" => NamedKey::FavoriteClear1,
        "favoriteclear2" => NamedKey::FavoriteClear2,
        "favoriteclear3" => NamedKey::FavoriteClear3,
        "favoriterecall0" => NamedKey::FavoriteRecall0,
        "favoriterecall1" => NamedKey::FavoriteRecall1,
        "favoriterecall2" => NamedKey::FavoriteRecall2,
        "favoriterecall3" => NamedKey::FavoriteRecall3,
        "favoritestore0" => NamedKey::FavoriteStore0,
        "favoritestore1" => NamedKey::FavoriteStore1,
        "favoritestore2" => NamedKey::FavoriteStore2,
        "favoritestore3" => NamedKey::FavoriteStore3,
        "guide" => NamedKey::Guide,
        "guidenextday" => NamedKey::GuideNextDay,
        "guidepreviousday" => NamedKey::GuidePreviousDay,
        "info" => NamedKey::Info,
        "instantreplay" => NamedKey::InstantReplay,
        "link" => NamedKey::Link,
        "listprogram" => NamedKey::ListProgram,
        "livecontent" => NamedKey::LiveContent,
        "lock" => NamedKey::Lock,
        "mediaapps" => NamedKey::MediaApps,
        "mediaaudiotrack" => NamedKey::MediaAudioTrack,
        "medialast" => NamedKey::MediaLast,
        "mediaskipbackward" => NamedKey::MediaSkipBackward,
        "mediaskipforward" => NamedKey::MediaSkipForward,
        "mediastepbackward" => NamedKey::MediaStepBackward,
        "mediastepforward" => NamedKey::MediaStepForward,
        "mediatopmenu" => NamedKey::MediaTopMenu,
        "navigatein" => NamedKey::NavigateIn,
        "navigatenext" => NamedKey::NavigateNext,
        "navigateout" => NamedKey::NavigateOut,
        "navigateprevious" => NamedKey::NavigatePrevious,
        "nextfavoritechannel" => NamedKey::NextFavoriteChannel,
        "nextuserprofile" => NamedKey::NextUserProfile,
        "ondemand" => NamedKey::OnDemand,
        "pairing" => NamedKey::Pairing,
        "pinpdown" => NamedKey::PinPDown,
        "pinpmove" => NamedKey::PinPMove,
        "pinptoggle" => NamedKey::PinPToggle,
        "pinpup" => NamedKey::PinPUp,
        "playspeeddown" => NamedKey::PlaySpeedDown,
        "playspeedreset" => NamedKey::PlaySpeedReset,
        "playspeedup" => NamedKey::PlaySpeedUp,
        "randomtoggle" => NamedKey::RandomToggle,
        "rclowbattery" => NamedKey::RcLowBattery,
        "recordspeednext" => NamedKey::RecordSpeedNext,
        "rfbypass" => NamedKey::RfBypass,
        "scanchannelstoggle" => NamedKey::ScanChannelsToggle,
        "screenmodenext" => NamedKey::ScreenModeNext,
        "settings" => NamedKey::Settings,
        "splitscreentoggle" => NamedKey::SplitScreenToggle,
        "stbinput" => NamedKey::STBInput,
        "stbpower" => NamedKey::STBPower,
        "subtitle" => NamedKey::Subtitle,
        "teletext" => NamedKey::Teletext,
        "videomodenext" => NamedKey::VideoModeNext,
        "wink" => NamedKey::Wink,
        "zoomtoggle" => NamedKey::ZoomToggle,
        "f1" => NamedKey::F1,
        "f2" => NamedKey::F2,
        "f3" => NamedKey::F3,
        "f4" => NamedKey::F4,
        "f5" => NamedKey::F5,
        "f6" => NamedKey::F6,
        "f7" => NamedKey::F7,
        "f8" => NamedKey::F8,
        "f9" => NamedKey::F9,
        "f10" => NamedKey::F10,
        "f11" => NamedKey::F11,
        "f12" => NamedKey::F12,
        "f13" => NamedKey::F13,
        "f14" => NamedKey::F14,
        "f15" => NamedKey::F15,
        "f16" => NamedKey::F16,
        "f17" => NamedKey::F17,
        "f18" => NamedKey::F18,
        "f19" => NamedKey::F19,
        "f20" => NamedKey::F20,
        "f21" => NamedKey::F21,
        "f22" => NamedKey::F22,
        "f23" => NamedKey::F23,
        "f24" => NamedKey::F24,
        "f25" => NamedKey::F25,
        "f26" => NamedKey::F26,
        "f27" => NamedKey::F27,
        "f28" => NamedKey::F28,
        "f29" => NamedKey::F29,
        "f30" => NamedKey::F30,
        "f31" => NamedKey::F31,
        "f32" => NamedKey::F32,
        "f33" => NamedKey::F33,
        "f34" => NamedKey::F34,
        "f35" => NamedKey::F35,
        _ => return None,
    };
    Some(key)
}

pub fn parse_keycode(text: &str) -> Option<KeyCode> {
    let key = match text {
        "backquote" | "`" => KeyCode::Backquote,
        "backslash" | r"\" => KeyCode::Backslash,
        "bracketleft" | "[" => KeyCode::BracketLeft,
        "bracketright" | "]" => KeyCode::BracketRight,
        "comma" | "," => KeyCode::Comma,
        "0" => KeyCode::Digit0,
        "1" => KeyCode::Digit1,
        "2" => KeyCode::Digit2,
        "3" => KeyCode::Digit3,
        "4" => KeyCode::Digit4,
        "5" => KeyCode::Digit5,
        "6" => KeyCode::Digit6,
        "7" => KeyCode::Digit7,
        "8" => KeyCode::Digit8,
        "9" => KeyCode::Digit9,
        "equal" | "=" => KeyCode::Equal,
        "intlbackslash" => KeyCode::IntlBackslash,
        "intlro" => KeyCode::IntlRo,
        "intlyen" => KeyCode::IntlYen,
        "a" => KeyCode::KeyA,
        "b" => KeyCode::KeyB,
        "c" => KeyCode::KeyC,
        "d" => KeyCode::KeyD,
        "e" => KeyCode::KeyE,
        "f" => KeyCode::KeyF,
        "g" => KeyCode::KeyG,
        "h" => KeyCode::KeyH,
        "i" => KeyCode::KeyI,
        "j" => KeyCode::KeyJ,
        "k" => KeyCode::KeyK,
        "l" => KeyCode::KeyL,
        "m" => KeyCode::KeyM,
        "n" => KeyCode::KeyN,
        "o" => KeyCode::KeyO,
        "p" => KeyCode::KeyP,
        "q" => KeyCode::KeyQ,
        "r" => KeyCode::KeyR,
        "s" => KeyCode::KeyS,
        "t" => KeyCode::KeyT,
        "u" => KeyCode::KeyU,
        "v" => KeyCode::KeyV,
        "w" => KeyCode::KeyW,
        "x" => KeyCode::KeyX,
        "y" => KeyCode::KeyY,
        "z" => KeyCode::KeyZ,
        "minus" | "-" => KeyCode::Minus,
        "period" | "." => KeyCode::Period,
        "quote" | "'" => KeyCode::Quote,
        "semicolon" | ";" => KeyCode::Semicolon,
        "slash" | "/" => KeyCode::Slash,
        "altleft" => KeyCode::AltLeft,
        "altright" => KeyCode::AltRight,
        "backspace" => KeyCode::Backspace,
        "capslock" => KeyCode::CapsLock,
        "contextmenu" => KeyCode::ContextMenu,
        "controlleft" => KeyCode::ControlLeft,
        "controlright" => KeyCode::ControlRight,
        "enter" => KeyCode::Enter,
        "superleft" => KeyCode::SuperLeft,
        "superright" => KeyCode::SuperRight,
        "shiftleft" => KeyCode::ShiftLeft,
        "shiftright" => KeyCode::ShiftRight,
        "space" => KeyCode::Space,
        "tab" => KeyCode::Tab,
        "convert" => KeyCode::Convert,
        "kanamode" => KeyCode::KanaMode,
        "lang1" => KeyCode::Lang1,
        "lang2" => KeyCode::Lang2,
        "lang3" => KeyCode::Lang3,
        "lang4" => KeyCode::Lang4,
        "lang5" => KeyCode::Lang5,
        "nonconvert" => KeyCode::NonConvert,
        "delete" => KeyCode::Delete,
        "end" => KeyCode::End,
        "help" => KeyCode::Help,
        "home" => KeyCode::Home,
        "insert" => KeyCode::Insert,
        "pagedown" | "pgdown" => KeyCode::PageDown,
        "pageup" | "pgup" => KeyCode::PageUp,
        "arrowdown" | "down" => KeyCode::ArrowDown,
        "arrowleft" | "left" => KeyCode::ArrowLeft,
        "arrowright" | "right" => KeyCode::ArrowRight,
        "arrowup" | "up" => KeyCode::ArrowUp,
        "numlock" => KeyCode::NumLock,
        "numpad0" => KeyCode::Numpad0,
        "numpad1" => KeyCode::Numpad1,
        "numpad2" => KeyCode::Numpad2,
        "numpad3" => KeyCode::Numpad3,
        "numpad4" => KeyCode::Numpad4,
        "numpad5" => KeyCode::Numpad5,
        "numpad6" => KeyCode::Numpad6,
        "numpad7" => KeyCode::Numpad7,
        "numpad8" => KeyCode::Numpad8,
        "numpad9" => KeyCode::Numpad9,
        "numpadadd" => KeyCode::NumpadAdd,
        "numpadbackspace" => KeyCode::NumpadBackspace,
        "numpadclear" => KeyCode::NumpadClear,
        "numpadclearentry" => KeyCode::NumpadClearEntry,
        "numpadcomma" => KeyCode::NumpadComma,
        "numpaddecimal" => KeyCode::NumpadDecimal,
        "numpaddivide" => KeyCode::NumpadDivide,
        "numpadenter" => KeyCode::NumpadEnter,
        "numpadequal" => KeyCode::NumpadEqual,
        "numpadhash" => KeyCode::NumpadHash,
        "numpadmemoryadd" => KeyCode::NumpadMemoryAdd,
        "numpadmemoryclear" => KeyCode::NumpadMemoryClear,
        "numpadmemoryrecall" => KeyCode::NumpadMemoryRecall,
        "numpadmemorystore" => KeyCode::NumpadMemoryStore,
        "numpadmemorysubtract" => KeyCode::NumpadMemorySubtract,
        "numpadmultiply" => KeyCode::NumpadMultiply,
        "numpadparenleft" => KeyCode::NumpadParenLeft,
        "numpadparenright" => KeyCode::NumpadParenRight,
        "numpadstar" => KeyCode::NumpadStar,
        "numpadsubtract" => KeyCode::NumpadSubtract,
        "escape" => KeyCode::Escape,
        "fn" => KeyCode::Fn,
        "fnlock" => KeyCode::FnLock,
        "printscreen" => KeyCode::PrintScreen,
        "scrolllock" => KeyCode::ScrollLock,
        "pause" => KeyCode::Pause,
        "browserback" => KeyCode::BrowserBack,
        "browserfavorites" => KeyCode::BrowserFavorites,
        "browserforward" => KeyCode::BrowserForward,
        "browserhome" => KeyCode::BrowserHome,
        "browserrefresh" => KeyCode::BrowserRefresh,
        "browsersearch" => KeyCode::BrowserSearch,
        "browserstop" => KeyCode::BrowserStop,
        "eject" => KeyCode::Eject,
        "launchapp1" => KeyCode::LaunchApp1,
        "launchapp2" => KeyCode::LaunchApp2,
        "launchmail" => KeyCode::LaunchMail,
        "mediaplaypause" => KeyCode::MediaPlayPause,
        "mediaselect" => KeyCode::MediaSelect,
        "mediastop" => KeyCode::MediaStop,
        "mediatracknext" => KeyCode::MediaTrackNext,
        "mediatrackprevious" => KeyCode::MediaTrackPrevious,
        "power" => KeyCode::Power,
        "sleep" => KeyCode::Sleep,
        "audiovolumedown" => KeyCode::AudioVolumeDown,
        "audiovolumemute" => KeyCode::AudioVolumeMute,
        "audiovolumeup" => KeyCode::AudioVolumeUp,
        "wakeup" => KeyCode::WakeUp,
        "meta" => KeyCode::Meta,
        "hyper" => KeyCode::Hyper,
        "turbo" => KeyCode::Turbo,
        "abort" => KeyCode::Abort,
        "resume" => KeyCode::Resume,
        "suspend" => KeyCode::Suspend,
        "again" => KeyCode::Again,
        "copy" => KeyCode::Copy,
        "cut" => KeyCode::Cut,
        "find" => KeyCode::Find,
        "open" => KeyCode::Open,
        "paste" => KeyCode::Paste,
        "props" => KeyCode::Props,
        "select" => KeyCode::Select,
        "undo" => KeyCode::Undo,
        "hiragana" => KeyCode::Hiragana,
        "katakana" => KeyCode::Katakana,
        "f1" => KeyCode::F1,
        "f2" => KeyCode::F2,
        "f3" => KeyCode::F3,
        "f4" => KeyCode::F4,
        "f5" => KeyCode::F5,
        "f6" => KeyCode::F6,
        "f7" => KeyCode::F7,
        "f8" => KeyCode::F8,
        "f9" => KeyCode::F9,
        "f10" => KeyCode::F10,
        "f11" => KeyCode::F11,
        "f12" => KeyCode::F12,
        "f13" => KeyCode::F13,
        "f14" => KeyCode::F14,
        "f15" => KeyCode::F15,
        "f16" => KeyCode::F16,
        "f17" => KeyCode::F17,
        "f18" => KeyCode::F18,
        "f19" => KeyCode::F19,
        "f20" => KeyCode::F20,
        "f21" => KeyCode::F21,
        "f22" => KeyCode::F22,
        "f23" => KeyCode::F23,
        "f24" => KeyCode::F24,
        "f25" => KeyCode::F25,
        "f26" => KeyCode::F26,
        "f27" => KeyCode::F27,
        "f28" => KeyCode::F28,
        "f29" => KeyCode::F29,
        "f30" => KeyCode::F30,
        "f31" => KeyCode::F31,
        "f32" => KeyCode::F32,
        "f33" => KeyCode::F33,
        "f34" => KeyCode::F34,
        "f35" => KeyCode::F35,
        _ => return None,
    };
    Some(key)
}

#[test]
fn parse_shortcut() {
    use crate::shortcut::{KeyCombination, Modifiers, ShortcutKey};

    fn test(text: &str, modifiers: Modifiers, key: impl Into<ShortcutKey>, equality: bool) {
        let actual = KeyCombination::from_str_portable(text).unwrap();
        let expected = KeyCombination::new(modifiers, key);
        if equality {
            assert_eq!(
                actual, expected,
                "parse({:?}): expected {:?}, got {:?}",
                text, expected, actual
            );
        } else {
            assert_ne!(
                actual, expected,
                "parse({:?}) for nonequality: expected {:?}, got {:?}",
                text, expected, actual
            );
        }
    }

    fn test_eq(text: &str, modifiers: Modifiers, key: impl Into<ShortcutKey>) {
        test(text, modifiers, key, true);
    }

    test_eq("PageDown", Modifiers::empty(), NamedKey::PageDown);
    test_eq("Left", Modifiers::empty(), NamedKey::ArrowLeft);
    test_eq("Ctrl+Left", Modifiers::CTRL_OR_MAC_CMD, NamedKey::ArrowLeft);
    test_eq(
        "Shift+Ctrl+Left",
        Modifiers::SHIFT | Modifiers::CTRL_OR_MAC_CMD,
        NamedKey::ArrowLeft,
    );
    test_eq("Ctrl+X", Modifiers::CTRL_OR_MAC_CMD, KeyCode::KeyX);
    test_eq("Ctrl+ X", Modifiers::CTRL_OR_MAC_CMD, KeyCode::KeyX);
    test_eq("Ctrl + X", Modifiers::CTRL_OR_MAC_CMD, KeyCode::KeyX);
    test_eq(" Ctrl+X", Modifiers::CTRL_OR_MAC_CMD, KeyCode::KeyX);
    test_eq("   Ctrl + X    ", Modifiers::CTRL_OR_MAC_CMD, KeyCode::KeyX);

    fn test_ne(text: &str, modifiers: Modifiers, key: impl Into<ShortcutKey>) {
        test(text, modifiers, key, false);
    }

    test_ne(
        "Ctrl+Left",
        Modifiers::META_OR_MAC_CTRL,
        NamedKey::ArrowLeft,
    );
    test_ne("PageDown", Modifiers::empty(), NamedKey::PageUp);

    fn test_for_err(text: &str, err: &str) {
        let error = KeyCombination::from_str_portable(text).err().unwrap();
        assert_eq!(
            &error.to_string(),
            err,
            "parse({:?}): expected error {:?}, got {:?}",
            text,
            err,
            error
        );
    }

    test_for_err("", "unknown key");
    test_for_err("+Shift", "invalid format");
    test_for_err("Alt++Shift", "invalid format");
    test_for_err("Blabla+Shift", "unknown modifier");
    test_for_err("Shift+Anotherblabla", "unknown key");
}