Skip to content

Commit 15d5510

Browse files
committed
Implement displaying localized warning messages for duplicate hotkeys in HotkeyControlDialog, and implement the ability to overwrite them
1 parent 737d29a commit 15d5510

File tree

5 files changed

+169
-46
lines changed

5 files changed

+169
-46
lines changed

Flow.Launcher.Infrastructure/Hotkey/IHotkeySettings.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,16 @@
22

33
namespace Flow.Launcher.Infrastructure.Hotkey;
44

5+
/// <summary>
6+
/// Interface that you should implement in your settings class to be able to pass it to
7+
/// <c>Flow.Launcher.HotkeyControlDialog</c>. It allows the dialog to display the hotkeys that have already been
8+
/// registered, and optionally provide a way to unregister them.
9+
/// </summary>
510
public interface IHotkeySettings
611
{
12+
/// <summary>
13+
/// A list of hotkeys that have already been registered. The dialog will display these hotkeys and provide a way to
14+
/// unregister them.
15+
/// </summary>
716
public List<RegisteredHotkeyData> RegisteredHotkeys { get; }
817
}

Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,116 @@ namespace Flow.Launcher.Infrastructure.Hotkey;
44

55
#nullable enable
66

7+
/// <summary>
8+
/// Represents a hotkey that has been registered. Used in <c>Flow.Launcher.HotkeyControlDialog</c> via
9+
/// <see cref="UserSettings"/> and <see cref="IHotkeySettings"/> to display errors if user tries to register a hotkey
10+
/// that has already been registered, and optionally provides a way to unregister the hotkey.
11+
/// </summary>
712
public record RegisteredHotkeyData
813
{
14+
/// <summary>
15+
/// <see cref="HotkeyModel"/> representation of this hotkey.
16+
/// </summary>
917
public HotkeyModel Hotkey { get; }
10-
public string Description { get; }
18+
19+
/// <summary>
20+
/// String key in the localization dictionary that represents this hotkey. For example, <c>ReloadPluginHotkey</c>,
21+
/// which represents the string "Reload Plugins Data" in <c>en.xaml</c>
22+
/// </summary>
23+
public string DescriptionResourceKey { get; }
24+
25+
/// <summary>
26+
/// Array of values that will replace <c>{0}</c>, <c>{1}</c>, <c>{2}</c>, etc. in the localized string found via
27+
/// <see cref="DescriptionResourceKey"/>.
28+
/// </summary>
29+
public object?[] DescriptionFormatVariables { get; } = Array.Empty<object?>();
30+
31+
/// <summary>
32+
/// An action that, when called, will unregister this hotkey. If it's <c>null</c>, it's assumed that
33+
/// this hotkey can't be unregistered, and the "Overwrite" option will not appear in the hotkey dialog.
34+
/// </summary>
1135
public Action? RemoveHotkey { get; }
1236

13-
public RegisteredHotkeyData(string hotkey, string description, Action? removeHotkey = null)
37+
/// <summary>
38+
/// Creates an instance of <c>RegisteredHotkeyData</c>. Assumes that the key specified in
39+
/// <c>descriptionResourceKey</c> doesn't need any arguments for <c>string.Format</c>. If it does,
40+
/// use one of the other constructors.
41+
/// </summary>
42+
/// <param name="hotkey">
43+
/// The hotkey this class will represent.
44+
/// Example values: <c>F1</c>, <c>Ctrl+Shift+Enter</c>
45+
/// </param>
46+
/// <param name="descriptionResourceKey">
47+
/// The key in the localization dictionary that represents this hotkey. For example, <c>ReloadPluginHotkey</c>,
48+
/// which represents the string "Reload Plugins Data" in <c>en.xaml</c>
49+
/// </param>
50+
/// <param name="removeHotkey">
51+
/// An action that, when called, will unregister this hotkey. If it's <c>null</c>, it's assumed that this hotkey
52+
/// can't be unregistered, and the "Overwrite" option will not appear in the hotkey dialog.
53+
/// </param>
54+
public RegisteredHotkeyData(string hotkey, string descriptionResourceKey, Action? removeHotkey = null)
55+
{
56+
Hotkey = new HotkeyModel(hotkey);
57+
DescriptionResourceKey = descriptionResourceKey;
58+
RemoveHotkey = removeHotkey;
59+
}
60+
61+
/// <summary>
62+
/// Creates an instance of <c>RegisteredHotkeyData</c>. Assumes that the key specified in
63+
/// <c>descriptionResourceKey</c> needs exactly one argument for <c>string.Format</c>.
64+
/// </summary>
65+
/// <param name="hotkey">
66+
/// The hotkey this class will represent.
67+
/// Example values: <c>F1</c>, <c>Ctrl+Shift+Enter</c>
68+
/// </param>
69+
/// <param name="descriptionResourceKey">
70+
/// The key in the localization dictionary that represents this hotkey. For example, <c>ReloadPluginHotkey</c>,
71+
/// which represents the string "Reload Plugins Data" in <c>en.xaml</c>
72+
/// </param>
73+
/// <param name="descriptionFormatVariable">
74+
/// The value that will replace <c>{0}</c> in the localized string found via <c>description</c>.
75+
/// </param>
76+
/// <param name="removeHotkey">
77+
/// An action that, when called, will unregister this hotkey. If it's <c>null</c>, it's assumed that this hotkey
78+
/// can't be unregistered, and the "Overwrite" option will not appear in the hotkey dialog.
79+
/// </param>
80+
public RegisteredHotkeyData(
81+
string hotkey, string descriptionResourceKey, object? descriptionFormatVariable, Action? removeHotkey = null
82+
)
83+
{
84+
Hotkey = new HotkeyModel(hotkey);
85+
DescriptionResourceKey = descriptionResourceKey;
86+
DescriptionFormatVariables = new[] { descriptionFormatVariable };
87+
RemoveHotkey = removeHotkey;
88+
}
89+
90+
/// <summary>
91+
/// Creates an instance of <c>RegisteredHotkeyData</c>. Assumes that the key specified in
92+
/// <paramref name="descriptionResourceKey"/> needs multiple arguments for <c>string.Format</c>.
93+
/// </summary>
94+
/// <param name="hotkey">
95+
/// The hotkey this class will represent.
96+
/// Example values: <c>F1</c>, <c>Ctrl+Shift+Enter</c>
97+
/// </param>
98+
/// <param name="descriptionResourceKey">
99+
/// The key in the localization dictionary that represents this hotkey. For example, <c>ReloadPluginHotkey</c>,
100+
/// which represents the string "Reload Plugins Data" in <c>en.xaml</c>
101+
/// </param>
102+
/// <param name="descriptionFormatVariables">
103+
/// Array of values that will replace <c>{0}</c>, <c>{1}</c>, <c>{2}</c>, etc.
104+
/// in the localized string found via <c>description</c>.
105+
/// </param>
106+
/// <param name="removeHotkey">
107+
/// An action that, when called, will unregister this hotkey. If it's <c>null</c>, it's assumed that this hotkey
108+
/// can't be unregistered, and the "Overwrite" option will not appear in the hotkey dialog.
109+
/// </param>
110+
public RegisteredHotkeyData(
111+
string hotkey, string descriptionResourceKey, object?[] descriptionFormatVariables, Action? removeHotkey = null
112+
)
14113
{
15114
Hotkey = new HotkeyModel(hotkey);
16-
Description = description;
115+
DescriptionResourceKey = descriptionResourceKey;
116+
DescriptionFormatVariables = descriptionFormatVariables;
17117
RemoveHotkey = removeHotkey;
18118
}
19119
}

Flow.Launcher.Infrastructure/UserSettings/Settings.cs

Lines changed: 45 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -282,65 +282,69 @@ public List<RegisteredHotkeyData> RegisteredHotkeys
282282
{
283283
var list = new List<RegisteredHotkeyData>
284284
{
285-
new("Escape", "Escape"), // TODO
286-
new("F5", "ReloadPluginHotkey"), // TODO
287-
new("Alt+Home", "Select last result"), // TODO
288-
new("Alt+End", "Select last result"), // TODO
289-
new("Ctrl+R", "Requery"), // TODO
290-
new("Ctrl+H", "ToggleHistoryHotkey"), // TODO
291-
new("Ctrl+OemCloseBrackets", "QuickWidthHotkey"), // TODO
292-
new("Ctrl+OemOpenBrackets", "QuickWidthHotkey"), // TODO
293-
new("Ctrl+OemPlus", "QuickHeightHotkey"), // TODO
294-
new("Ctrl+OemMinus", "QuickHeightHotkey"), // TODO
295-
new("Ctrl+Shift+Enter", "HotkeyCtrlShiftEnterDesc"), // TODO
296-
new("Shift+Enter", "OpenContextMenuHotkey"), // TODO
297-
new("Enter", "HotkeyRunDesc"), // TODO
298-
new("Ctrl+Enter", "Open result"), // TODO
299-
new("Alt+Enter", "Open result"), // TODO
300-
new("Ctrl+F12", "ToggleGameModeHotkey"), // TODO
301-
new("Ctrl+Shift+C", "Copy alternative"), // TODO
302-
303-
new($"{OpenResultModifiers}+D1", "Open Result"), // TODO
304-
new($"{OpenResultModifiers}+D2", "Open Result"), // TODO
305-
new($"{OpenResultModifiers}+D3", "Open Result"), // TODO
306-
new($"{OpenResultModifiers}+D4", "Open Result"), // TODO
307-
new($"{OpenResultModifiers}+D5", "Open Result"), // TODO
308-
new($"{OpenResultModifiers}+D6", "Open Result"), // TODO
309-
new($"{OpenResultModifiers}+D7", "Open Result"), // TODO
310-
new($"{OpenResultModifiers}+D8", "Open Result"), // TODO
311-
new($"{OpenResultModifiers}+D9", "Open Result"), // TODO
312-
new($"{OpenResultModifiers}+D0", "Open Result"), // TODO
285+
new("Up", "HotkeyLeftRightDesc"),
286+
new("Down", "HotkeyLeftRightDesc"),
287+
new("Left", "HotkeyUpDownDesc"),
288+
new("Right", "HotkeyUpDownDesc"),
289+
new("Escape", "HotkeyESCDesc"),
290+
new("F5", "ReloadPluginHotkey"),
291+
new("Alt+Home", "HotkeySelectFirstResult"),
292+
new("Alt+End", "HotkeySelectLastResult"),
293+
new("Ctrl+R", "HotkeyRequery"),
294+
new("Ctrl+H", "ToggleHistoryHotkey"),
295+
new("Ctrl+OemCloseBrackets", "QuickWidthHotkey"),
296+
new("Ctrl+OemOpenBrackets", "QuickWidthHotkey"),
297+
new("Ctrl+OemPlus", "QuickHeightHotkey"),
298+
new("Ctrl+OemMinus", "QuickHeightHotkey"),
299+
new("Ctrl+Shift+Enter", "HotkeyCtrlShiftEnterDesc"),
300+
new("Shift+Enter", "OpenContextMenuHotkey"),
301+
new("Enter", "HotkeyRunDesc"),
302+
new("Ctrl+Enter", "OpenContainFolderHotkey"),
303+
new("Alt+Enter", "HotkeyOpenResult"),
304+
new("Ctrl+F12", "ToggleGameModeHotkey"),
305+
new("Ctrl+Shift+C", "CopyFilePathHotkey"),
306+
307+
new($"{OpenResultModifiers}+D1", "HotkeyOpenResultN", 1),
308+
new($"{OpenResultModifiers}+D2", "HotkeyOpenResultN", 2),
309+
new($"{OpenResultModifiers}+D3", "HotkeyOpenResultN", 3),
310+
new($"{OpenResultModifiers}+D4", "HotkeyOpenResultN", 4),
311+
new($"{OpenResultModifiers}+D5", "HotkeyOpenResultN", 5),
312+
new($"{OpenResultModifiers}+D6", "HotkeyOpenResultN", 6),
313+
new($"{OpenResultModifiers}+D7", "HotkeyOpenResultN", 7),
314+
new($"{OpenResultModifiers}+D8", "HotkeyOpenResultN", 8),
315+
new($"{OpenResultModifiers}+D9", "HotkeyOpenResultN", 9),
316+
new($"{OpenResultModifiers}+D0", "HotkeyOpenResultN", 10)
313317
};
314318

315319
if(!string.IsNullOrEmpty(Hotkey))
316-
list.Add(new(Hotkey, "Open main window", () => Hotkey = "")); // TODO
320+
list.Add(new(Hotkey, "flowlauncherHotkey", () => Hotkey = ""));
317321
if(!string.IsNullOrEmpty(PreviewHotkey))
318-
list.Add(new(PreviewHotkey, "Preview Hotkey", () => PreviewHotkey = "")); // TODO
322+
list.Add(new(PreviewHotkey, "previewHotkey", () => PreviewHotkey = ""));
319323
if(!string.IsNullOrEmpty(AutoCompleteHotkey))
320-
list.Add(new(AutoCompleteHotkey, "AutoCompleteHotkey", () => AutoCompleteHotkey = "")); // TODO
324+
list.Add(new(AutoCompleteHotkey, "autoCompleteHotkey", () => AutoCompleteHotkey = ""));
321325
if(!string.IsNullOrEmpty(AutoCompleteHotkey2))
322-
list.Add(new(AutoCompleteHotkey2, "AutoCompleteHotkey", () => AutoCompleteHotkey2 = "")); // TODO
326+
list.Add(new(AutoCompleteHotkey2, "autoCompleteHotkey", () => AutoCompleteHotkey2 = ""));
323327
if(!string.IsNullOrEmpty(SelectNextItemHotkey))
324-
list.Add(new(SelectNextItemHotkey, "SelectNextItemHotkey", () => SelectNextItemHotkey = "")); // TODO
328+
list.Add(new(SelectNextItemHotkey, "SelectNextItemHotkey", () => SelectNextItemHotkey = ""));
325329
if(!string.IsNullOrEmpty(SelectNextItemHotkey2))
326-
list.Add(new(SelectNextItemHotkey2, "SelectNextItemHotkey", () => SelectNextItemHotkey2 = "")); // TODO
330+
list.Add(new(SelectNextItemHotkey2, "SelectNextItemHotkey", () => SelectNextItemHotkey2 = ""));
327331
if(!string.IsNullOrEmpty(SelectPrevItemHotkey))
328-
list.Add(new(SelectPrevItemHotkey, "SelectPrevItemHotkey", () => SelectPrevItemHotkey = "")); // TODO
332+
list.Add(new(SelectPrevItemHotkey, "SelectPrevItemHotkey", () => SelectPrevItemHotkey = ""));
329333
if(!string.IsNullOrEmpty(SelectPrevItemHotkey2))
330-
list.Add(new(SelectPrevItemHotkey2, "SelectPrevItemHotkey", () => SelectPrevItemHotkey2 = "")); // TODO
334+
list.Add(new(SelectPrevItemHotkey2, "SelectPrevItemHotkey", () => SelectPrevItemHotkey2 = ""));
331335
if(!string.IsNullOrEmpty(SettingWindowHotkey))
332-
list.Add(new(SettingWindowHotkey, "SettingWindowHotkey", () => SettingWindowHotkey = "")); // TODO
336+
list.Add(new(SettingWindowHotkey, "SettingWindowHotkey", () => SettingWindowHotkey = ""));
333337
if(!string.IsNullOrEmpty(OpenContextMenuHotkey))
334-
list.Add(new(OpenContextMenuHotkey, "OpenContextMenuHotkey", () => OpenContextMenuHotkey = "")); // TODO
338+
list.Add(new(OpenContextMenuHotkey, "OpenContextMenuHotkey", () => OpenContextMenuHotkey = ""));
335339
if(!string.IsNullOrEmpty(SelectNextPageHotkey))
336-
list.Add(new(SelectNextPageHotkey, "SelectNextPageHotkey", () => SelectNextPageHotkey = "")); // TODO
340+
list.Add(new(SelectNextPageHotkey, "SelectNextPageHotkey", () => SelectNextPageHotkey = ""));
337341
if(!string.IsNullOrEmpty(SelectPrevPageHotkey))
338-
list.Add(new(SelectPrevPageHotkey, "SelectPrevPageHotkey", () => SelectPrevPageHotkey = "")); // TODO
342+
list.Add(new(SelectPrevPageHotkey, "SelectPrevPageHotkey", () => SelectPrevPageHotkey = ""));
339343

340344
foreach (var customPluginHotkey in CustomPluginHotkeys)
341345
{
342346
if (!string.IsNullOrEmpty(customPluginHotkey.Hotkey))
343-
list.Add(new(customPluginHotkey.Hotkey, "Custom plugin hotkey", () => customPluginHotkey.Hotkey = "")); // TODO
347+
list.Add(new(customPluginHotkey.Hotkey, "customQueryHotkey", () => customPluginHotkey.Hotkey = ""));
344348
}
345349

346350
return list;

Flow.Launcher/HotkeyControlDialog.xaml.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,16 @@ private void SetKeysToDisplay(HotkeyModel? hotkey)
119119

120120
if (_hotkeySettings.RegisteredHotkeys.FirstOrDefault(v => v.Hotkey == hotkey) is { } registeredHotkeyData)
121121
{
122+
var description = string.Format(
123+
InternationalizationManager.Instance.GetTranslation(registeredHotkeyData.DescriptionResourceKey),
124+
registeredHotkeyData.DescriptionFormatVariables
125+
);
122126
Alert.Visibility = Visibility.Visible;
123127
if (registeredHotkeyData.RemoveHotkey is not null)
124128
{
125129
tbMsg.Text = string.Format(
126130
InternationalizationManager.Instance.GetTranslation("hotkeyUnavailableEditable"),
127-
registeredHotkeyData.Description
131+
description
128132
);
129133
SaveBtn.IsEnabled = false;
130134
SaveBtn.Visibility = Visibility.Collapsed;
@@ -136,7 +140,7 @@ private void SetKeysToDisplay(HotkeyModel? hotkey)
136140
{
137141
tbMsg.Text = string.Format(
138142
InternationalizationManager.Instance.GetTranslation("hotkeyUnavailableUneditable"),
139-
registeredHotkeyData.Description
143+
description
140144
);
141145
SaveBtn.IsEnabled = false;
142146
SaveBtn.Visibility = Visibility.Visible;

Flow.Launcher/Languages/en.xaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,12 @@
398398
<system:String x:Key="HotkeyCtrlIDesc">Open Setting Window</system:String>
399399
<system:String x:Key="HotkeyF5Desc">Reload Plugin Data</system:String>
400400

401+
<system:String x:Key="HotkeySelectFirstResult">Select first result</system:String>
402+
<system:String x:Key="HotkeySelectLastResult">Select last result</system:String>
403+
<system:String x:Key="HotkeyRequery">Run current query again</system:String>
404+
<system:String x:Key="HotkeyOpenResult">Open result</system:String>
405+
<system:String x:Key="HotkeyOpenResultN">Open result #{0}</system:String>
406+
401407
<system:String x:Key="RecommendWeather">Weather</system:String>
402408
<system:String x:Key="RecommendWeatherDesc">Weather in Google Result</system:String>
403409
<system:String x:Key="RecommendShell">&gt; ping 8.8.8.8</system:String>

0 commit comments

Comments
 (0)