Implement displaying localized warning messages for duplicate hotkeys in HotkeyControlDialog, and implement the ability to overwrite them

This commit is contained in:
Yusyuriv 2024-04-26 11:42:20 +06:00
parent 737d29a0fb
commit 15d5510fae
No known key found for this signature in database
GPG key ID: A91C52E6F73148E0
5 changed files with 168 additions and 45 deletions

View file

@ -2,7 +2,16 @@
namespace Flow.Launcher.Infrastructure.Hotkey;
/// <summary>
/// Interface that you should implement in your settings class to be able to pass it to
/// <c>Flow.Launcher.HotkeyControlDialog</c>. It allows the dialog to display the hotkeys that have already been
/// registered, and optionally provide a way to unregister them.
/// </summary>
public interface IHotkeySettings
{
/// <summary>
/// A list of hotkeys that have already been registered. The dialog will display these hotkeys and provide a way to
/// unregister them.
/// </summary>
public List<RegisteredHotkeyData> RegisteredHotkeys { get; }
}

View file

@ -4,16 +4,116 @@ namespace Flow.Launcher.Infrastructure.Hotkey;
#nullable enable
/// <summary>
/// Represents a hotkey that has been registered. Used in <c>Flow.Launcher.HotkeyControlDialog</c> via
/// <see cref="UserSettings"/> and <see cref="IHotkeySettings"/> to display errors if user tries to register a hotkey
/// that has already been registered, and optionally provides a way to unregister the hotkey.
/// </summary>
public record RegisteredHotkeyData
{
/// <summary>
/// <see cref="HotkeyModel"/> representation of this hotkey.
/// </summary>
public HotkeyModel Hotkey { get; }
public string Description { get; }
/// <summary>
/// String key in the localization dictionary that represents this hotkey. For example, <c>ReloadPluginHotkey</c>,
/// which represents the string "Reload Plugins Data" in <c>en.xaml</c>
/// </summary>
public string DescriptionResourceKey { get; }
/// <summary>
/// Array of values that will replace <c>{0}</c>, <c>{1}</c>, <c>{2}</c>, etc. in the localized string found via
/// <see cref="DescriptionResourceKey"/>.
/// </summary>
public object?[] DescriptionFormatVariables { get; } = Array.Empty<object?>();
/// <summary>
/// An action that, when called, will unregister this hotkey. If it's <c>null</c>, it's assumed that
/// this hotkey can't be unregistered, and the "Overwrite" option will not appear in the hotkey dialog.
/// </summary>
public Action? RemoveHotkey { get; }
public RegisteredHotkeyData(string hotkey, string description, Action? removeHotkey = null)
/// <summary>
/// Creates an instance of <c>RegisteredHotkeyData</c>. Assumes that the key specified in
/// <c>descriptionResourceKey</c> doesn't need any arguments for <c>string.Format</c>. If it does,
/// use one of the other constructors.
/// </summary>
/// <param name="hotkey">
/// The hotkey this class will represent.
/// Example values: <c>F1</c>, <c>Ctrl+Shift+Enter</c>
/// </param>
/// <param name="descriptionResourceKey">
/// The key in the localization dictionary that represents this hotkey. For example, <c>ReloadPluginHotkey</c>,
/// which represents the string "Reload Plugins Data" in <c>en.xaml</c>
/// </param>
/// <param name="removeHotkey">
/// An action that, when called, will unregister this hotkey. If it's <c>null</c>, it's assumed that this hotkey
/// can't be unregistered, and the "Overwrite" option will not appear in the hotkey dialog.
/// </param>
public RegisteredHotkeyData(string hotkey, string descriptionResourceKey, Action? removeHotkey = null)
{
Hotkey = new HotkeyModel(hotkey);
Description = description;
DescriptionResourceKey = descriptionResourceKey;
RemoveHotkey = removeHotkey;
}
/// <summary>
/// Creates an instance of <c>RegisteredHotkeyData</c>. Assumes that the key specified in
/// <c>descriptionResourceKey</c> needs exactly one argument for <c>string.Format</c>.
/// </summary>
/// <param name="hotkey">
/// The hotkey this class will represent.
/// Example values: <c>F1</c>, <c>Ctrl+Shift+Enter</c>
/// </param>
/// <param name="descriptionResourceKey">
/// The key in the localization dictionary that represents this hotkey. For example, <c>ReloadPluginHotkey</c>,
/// which represents the string "Reload Plugins Data" in <c>en.xaml</c>
/// </param>
/// <param name="descriptionFormatVariable">
/// The value that will replace <c>{0}</c> in the localized string found via <c>description</c>.
/// </param>
/// <param name="removeHotkey">
/// An action that, when called, will unregister this hotkey. If it's <c>null</c>, it's assumed that this hotkey
/// can't be unregistered, and the "Overwrite" option will not appear in the hotkey dialog.
/// </param>
public RegisteredHotkeyData(
string hotkey, string descriptionResourceKey, object? descriptionFormatVariable, Action? removeHotkey = null
)
{
Hotkey = new HotkeyModel(hotkey);
DescriptionResourceKey = descriptionResourceKey;
DescriptionFormatVariables = new[] { descriptionFormatVariable };
RemoveHotkey = removeHotkey;
}
/// <summary>
/// Creates an instance of <c>RegisteredHotkeyData</c>. Assumes that the key specified in
/// <paramref name="descriptionResourceKey"/> needs multiple arguments for <c>string.Format</c>.
/// </summary>
/// <param name="hotkey">
/// The hotkey this class will represent.
/// Example values: <c>F1</c>, <c>Ctrl+Shift+Enter</c>
/// </param>
/// <param name="descriptionResourceKey">
/// The key in the localization dictionary that represents this hotkey. For example, <c>ReloadPluginHotkey</c>,
/// which represents the string "Reload Plugins Data" in <c>en.xaml</c>
/// </param>
/// <param name="descriptionFormatVariables">
/// Array of values that will replace <c>{0}</c>, <c>{1}</c>, <c>{2}</c>, etc.
/// in the localized string found via <c>description</c>.
/// </param>
/// <param name="removeHotkey">
/// An action that, when called, will unregister this hotkey. If it's <c>null</c>, it's assumed that this hotkey
/// can't be unregistered, and the "Overwrite" option will not appear in the hotkey dialog.
/// </param>
public RegisteredHotkeyData(
string hotkey, string descriptionResourceKey, object?[] descriptionFormatVariables, Action? removeHotkey = null
)
{
Hotkey = new HotkeyModel(hotkey);
DescriptionResourceKey = descriptionResourceKey;
DescriptionFormatVariables = descriptionFormatVariables;
RemoveHotkey = removeHotkey;
}
}

View file

@ -282,65 +282,69 @@ namespace Flow.Launcher.Infrastructure.UserSettings
{
var list = new List<RegisteredHotkeyData>
{
new("Escape", "Escape"), // TODO
new("F5", "ReloadPluginHotkey"), // TODO
new("Alt+Home", "Select last result"), // TODO
new("Alt+End", "Select last result"), // TODO
new("Ctrl+R", "Requery"), // TODO
new("Ctrl+H", "ToggleHistoryHotkey"), // TODO
new("Ctrl+OemCloseBrackets", "QuickWidthHotkey"), // TODO
new("Ctrl+OemOpenBrackets", "QuickWidthHotkey"), // TODO
new("Ctrl+OemPlus", "QuickHeightHotkey"), // TODO
new("Ctrl+OemMinus", "QuickHeightHotkey"), // TODO
new("Ctrl+Shift+Enter", "HotkeyCtrlShiftEnterDesc"), // TODO
new("Shift+Enter", "OpenContextMenuHotkey"), // TODO
new("Enter", "HotkeyRunDesc"), // TODO
new("Ctrl+Enter", "Open result"), // TODO
new("Alt+Enter", "Open result"), // TODO
new("Ctrl+F12", "ToggleGameModeHotkey"), // TODO
new("Ctrl+Shift+C", "Copy alternative"), // TODO
new("Up", "HotkeyLeftRightDesc"),
new("Down", "HotkeyLeftRightDesc"),
new("Left", "HotkeyUpDownDesc"),
new("Right", "HotkeyUpDownDesc"),
new("Escape", "HotkeyESCDesc"),
new("F5", "ReloadPluginHotkey"),
new("Alt+Home", "HotkeySelectFirstResult"),
new("Alt+End", "HotkeySelectLastResult"),
new("Ctrl+R", "HotkeyRequery"),
new("Ctrl+H", "ToggleHistoryHotkey"),
new("Ctrl+OemCloseBrackets", "QuickWidthHotkey"),
new("Ctrl+OemOpenBrackets", "QuickWidthHotkey"),
new("Ctrl+OemPlus", "QuickHeightHotkey"),
new("Ctrl+OemMinus", "QuickHeightHotkey"),
new("Ctrl+Shift+Enter", "HotkeyCtrlShiftEnterDesc"),
new("Shift+Enter", "OpenContextMenuHotkey"),
new("Enter", "HotkeyRunDesc"),
new("Ctrl+Enter", "OpenContainFolderHotkey"),
new("Alt+Enter", "HotkeyOpenResult"),
new("Ctrl+F12", "ToggleGameModeHotkey"),
new("Ctrl+Shift+C", "CopyFilePathHotkey"),
new($"{OpenResultModifiers}+D1", "Open Result"), // TODO
new($"{OpenResultModifiers}+D2", "Open Result"), // TODO
new($"{OpenResultModifiers}+D3", "Open Result"), // TODO
new($"{OpenResultModifiers}+D4", "Open Result"), // TODO
new($"{OpenResultModifiers}+D5", "Open Result"), // TODO
new($"{OpenResultModifiers}+D6", "Open Result"), // TODO
new($"{OpenResultModifiers}+D7", "Open Result"), // TODO
new($"{OpenResultModifiers}+D8", "Open Result"), // TODO
new($"{OpenResultModifiers}+D9", "Open Result"), // TODO
new($"{OpenResultModifiers}+D0", "Open Result"), // TODO
new($"{OpenResultModifiers}+D1", "HotkeyOpenResultN", 1),
new($"{OpenResultModifiers}+D2", "HotkeyOpenResultN", 2),
new($"{OpenResultModifiers}+D3", "HotkeyOpenResultN", 3),
new($"{OpenResultModifiers}+D4", "HotkeyOpenResultN", 4),
new($"{OpenResultModifiers}+D5", "HotkeyOpenResultN", 5),
new($"{OpenResultModifiers}+D6", "HotkeyOpenResultN", 6),
new($"{OpenResultModifiers}+D7", "HotkeyOpenResultN", 7),
new($"{OpenResultModifiers}+D8", "HotkeyOpenResultN", 8),
new($"{OpenResultModifiers}+D9", "HotkeyOpenResultN", 9),
new($"{OpenResultModifiers}+D0", "HotkeyOpenResultN", 10)
};
if(!string.IsNullOrEmpty(Hotkey))
list.Add(new(Hotkey, "Open main window", () => Hotkey = "")); // TODO
list.Add(new(Hotkey, "flowlauncherHotkey", () => Hotkey = ""));
if(!string.IsNullOrEmpty(PreviewHotkey))
list.Add(new(PreviewHotkey, "Preview Hotkey", () => PreviewHotkey = "")); // TODO
list.Add(new(PreviewHotkey, "previewHotkey", () => PreviewHotkey = ""));
if(!string.IsNullOrEmpty(AutoCompleteHotkey))
list.Add(new(AutoCompleteHotkey, "AutoCompleteHotkey", () => AutoCompleteHotkey = "")); // TODO
list.Add(new(AutoCompleteHotkey, "autoCompleteHotkey", () => AutoCompleteHotkey = ""));
if(!string.IsNullOrEmpty(AutoCompleteHotkey2))
list.Add(new(AutoCompleteHotkey2, "AutoCompleteHotkey", () => AutoCompleteHotkey2 = "")); // TODO
list.Add(new(AutoCompleteHotkey2, "autoCompleteHotkey", () => AutoCompleteHotkey2 = ""));
if(!string.IsNullOrEmpty(SelectNextItemHotkey))
list.Add(new(SelectNextItemHotkey, "SelectNextItemHotkey", () => SelectNextItemHotkey = "")); // TODO
list.Add(new(SelectNextItemHotkey, "SelectNextItemHotkey", () => SelectNextItemHotkey = ""));
if(!string.IsNullOrEmpty(SelectNextItemHotkey2))
list.Add(new(SelectNextItemHotkey2, "SelectNextItemHotkey", () => SelectNextItemHotkey2 = "")); // TODO
list.Add(new(SelectNextItemHotkey2, "SelectNextItemHotkey", () => SelectNextItemHotkey2 = ""));
if(!string.IsNullOrEmpty(SelectPrevItemHotkey))
list.Add(new(SelectPrevItemHotkey, "SelectPrevItemHotkey", () => SelectPrevItemHotkey = "")); // TODO
list.Add(new(SelectPrevItemHotkey, "SelectPrevItemHotkey", () => SelectPrevItemHotkey = ""));
if(!string.IsNullOrEmpty(SelectPrevItemHotkey2))
list.Add(new(SelectPrevItemHotkey2, "SelectPrevItemHotkey", () => SelectPrevItemHotkey2 = "")); // TODO
list.Add(new(SelectPrevItemHotkey2, "SelectPrevItemHotkey", () => SelectPrevItemHotkey2 = ""));
if(!string.IsNullOrEmpty(SettingWindowHotkey))
list.Add(new(SettingWindowHotkey, "SettingWindowHotkey", () => SettingWindowHotkey = "")); // TODO
list.Add(new(SettingWindowHotkey, "SettingWindowHotkey", () => SettingWindowHotkey = ""));
if(!string.IsNullOrEmpty(OpenContextMenuHotkey))
list.Add(new(OpenContextMenuHotkey, "OpenContextMenuHotkey", () => OpenContextMenuHotkey = "")); // TODO
list.Add(new(OpenContextMenuHotkey, "OpenContextMenuHotkey", () => OpenContextMenuHotkey = ""));
if(!string.IsNullOrEmpty(SelectNextPageHotkey))
list.Add(new(SelectNextPageHotkey, "SelectNextPageHotkey", () => SelectNextPageHotkey = "")); // TODO
list.Add(new(SelectNextPageHotkey, "SelectNextPageHotkey", () => SelectNextPageHotkey = ""));
if(!string.IsNullOrEmpty(SelectPrevPageHotkey))
list.Add(new(SelectPrevPageHotkey, "SelectPrevPageHotkey", () => SelectPrevPageHotkey = "")); // TODO
list.Add(new(SelectPrevPageHotkey, "SelectPrevPageHotkey", () => SelectPrevPageHotkey = ""));
foreach (var customPluginHotkey in CustomPluginHotkeys)
{
if (!string.IsNullOrEmpty(customPluginHotkey.Hotkey))
list.Add(new(customPluginHotkey.Hotkey, "Custom plugin hotkey", () => customPluginHotkey.Hotkey = "")); // TODO
list.Add(new(customPluginHotkey.Hotkey, "customQueryHotkey", () => customPluginHotkey.Hotkey = ""));
}
return list;

View file

@ -119,12 +119,16 @@ public partial class HotkeyControlDialog : ContentDialog
if (_hotkeySettings.RegisteredHotkeys.FirstOrDefault(v => v.Hotkey == hotkey) is { } registeredHotkeyData)
{
var description = string.Format(
InternationalizationManager.Instance.GetTranslation(registeredHotkeyData.DescriptionResourceKey),
registeredHotkeyData.DescriptionFormatVariables
);
Alert.Visibility = Visibility.Visible;
if (registeredHotkeyData.RemoveHotkey is not null)
{
tbMsg.Text = string.Format(
InternationalizationManager.Instance.GetTranslation("hotkeyUnavailableEditable"),
registeredHotkeyData.Description
description
);
SaveBtn.IsEnabled = false;
SaveBtn.Visibility = Visibility.Collapsed;
@ -136,7 +140,7 @@ public partial class HotkeyControlDialog : ContentDialog
{
tbMsg.Text = string.Format(
InternationalizationManager.Instance.GetTranslation("hotkeyUnavailableUneditable"),
registeredHotkeyData.Description
description
);
SaveBtn.IsEnabled = false;
SaveBtn.Visibility = Visibility.Visible;

View file

@ -398,6 +398,12 @@
<system:String x:Key="HotkeyCtrlIDesc">Open Setting Window</system:String>
<system:String x:Key="HotkeyF5Desc">Reload Plugin Data</system:String>
<system:String x:Key="HotkeySelectFirstResult">Select first result</system:String>
<system:String x:Key="HotkeySelectLastResult">Select last result</system:String>
<system:String x:Key="HotkeyRequery">Run current query again</system:String>
<system:String x:Key="HotkeyOpenResult">Open result</system:String>
<system:String x:Key="HotkeyOpenResultN">Open result #{0}</system:String>
<system:String x:Key="RecommendWeather">Weather</system:String>
<system:String x:Key="RecommendWeatherDesc">Weather in Google Result</system:String>
<system:String x:Key="RecommendShell">&gt; ping 8.8.8.8</system:String>