From de8036bdba3c272ef485bc7addf39fea75c31d0d Mon Sep 17 00:00:00 2001 From: Yusyuriv Date: Wed, 24 Apr 2024 20:23:11 +0600 Subject: [PATCH 1/3] Start working on handling duplicate hotkeys --- Flow.Launcher/HotkeyControlDialog.xaml | 38 ++++++++++-------- Flow.Launcher/HotkeyControlDialog.xaml.cs | 47 +++++++++++++++++++++-- Flow.Launcher/Languages/en.xaml | 2 + 3 files changed, 67 insertions(+), 20 deletions(-) diff --git a/Flow.Launcher/HotkeyControlDialog.xaml b/Flow.Launcher/HotkeyControlDialog.xaml index 322f82366..1eed711f3 100644 --- a/Flow.Launcher/HotkeyControlDialog.xaml +++ b/Flow.Launcher/HotkeyControlDialog.xaml @@ -82,7 +82,8 @@ - - - - + + + + + + diff --git a/Flow.Launcher/HotkeyControlDialog.xaml.cs b/Flow.Launcher/HotkeyControlDialog.xaml.cs index afb7d4229..f4e04e38b 100644 --- a/Flow.Launcher/HotkeyControlDialog.xaml.cs +++ b/Flow.Launcher/HotkeyControlDialog.xaml.cs @@ -1,4 +1,5 @@ -using System.Collections.ObjectModel; +using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Windows; using System.Windows.Input; using Flow.Launcher.Core.Resource; @@ -16,6 +17,28 @@ public partial class HotkeyControlDialog : ContentDialog public HotkeyModel CurrentHotkey { get; private set; } public ObservableCollection KeysToDisplay { get; } = new(); + private readonly Dictionary StaticHotkeys = new() + { + [new HotkeyModel("Escape")] = "", // TODO + [new HotkeyModel("F5")] = "ReloadPluginHotkey", + [new HotkeyModel("Alt+Home")] = "Select first result", // TODO + [new HotkeyModel("Alt+End")] = "Select last result", // TODO + [new HotkeyModel("Ctrl+R")] = "Requery", // TODO + [new HotkeyModel("Ctrl+H")] = "ToggleHistoryHotkey", + [new HotkeyModel("Ctrl+OemCloseBrackets")] = "QuickWidthHotkey", + [new HotkeyModel("Ctrl+OemOpenBrackets")] = "QuickWidthHotkey", + [new HotkeyModel("Ctrl+OemPlus")] = "QuickHeightHotkey", + [new HotkeyModel("Ctrl+OemMinus")] = "QuickHeightHotkey", + [new HotkeyModel("Ctrl+Shift+Enter")] = "HotkeyCtrlShiftEnterDesc", + [new HotkeyModel("Shift+Enter")] = "OpenContextMenuHotkey", + [new HotkeyModel("Enter")] = "HotkeyRunDesc", + [new HotkeyModel("Ctrl+Enter")] = "Open result", // TODO + [new HotkeyModel("Alt+Enter")] = "Open result", // TODO + // TODO D0-D9 But not here since they're not completely static, they can be Ctrl+D0-D9, Alt+D0-D9, or Ctrl+Alt+D0-D9 + [new HotkeyModel("Ctrl+F12")] = "ToggleGameModeHotkey", + [new HotkeyModel("Ctrl+Shift+C")] = "Copy alternative", // TODO + }; + public enum EResultType { Cancel, @@ -109,11 +132,20 @@ public partial class HotkeyControlDialog : ContentDialog if (tbMsg == null) return; + + if (StaticHotkeys.TryGetValue((HotkeyModel)hotkey, out var staticHotkey)) + { + ShowWarningAndDisableSaveButton( + string.Format( + InternationalizationManager.Instance.GetTranslation("hotkeyUnavailableInUseStatic"), + InternationalizationManager.Instance.GetTranslation(staticHotkey) + ) + ); + return; + } if (!CheckHotkeyAvailability(hotkey.Value, true)) { - tbMsg.Text = InternationalizationManager.Instance.GetTranslation("hotkeyUnavailable"); - Alert.Visibility = Visibility.Visible; - SaveBtn.IsEnabled = false; + ShowWarningAndDisableSaveButton(InternationalizationManager.Instance.GetTranslation("hotkeyUnavailable")); } else { @@ -122,6 +154,13 @@ public partial class HotkeyControlDialog : ContentDialog } } + private void ShowWarningAndDisableSaveButton(string message) + { + tbMsg.Text = message; + Alert.Visibility = Visibility.Visible; + SaveBtn.IsEnabled = false; + } + private static bool CheckHotkeyAvailability(HotkeyModel hotkey, bool validateKeyGesture) => hotkey.Validate(validateKeyGesture) && HotKeyMapper.CheckAvailability(hotkey); } diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index b71e25e8f..25d618726 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -311,6 +311,8 @@ Update Binding Hotkey Current hotkey is unavailable. + This hotkey is unavailable because it is reserved for Flow Launcher's functionality: {0}. + This hotkey is unavailable because it is already used for {0}. Press the keys you want to use for this function. From 737d29a0fba78b39858b44f16af181151dd5db81 Mon Sep 17 00:00:00 2001 From: Yusyuriv Date: Thu, 25 Apr 2024 14:02:05 +0600 Subject: [PATCH 2/3] Implement checking for duplicate hotkeys and overwriting them --- .../Hotkey/IHotkeySettings.cs | 8 ++ .../Hotkey/RegisteredHotkeyData.cs | 19 ++++ .../UserSettings/Settings.cs | 89 +++++++++++++++++-- Flow.Launcher/CustomQueryHotkeySetting.xaml | 1 + .../CustomQueryHotkeySetting.xaml.cs | 10 +-- Flow.Launcher/HotkeyControl.xaml.cs | 13 ++- Flow.Launcher/HotkeyControlDialog.xaml | 10 +++ Flow.Launcher/HotkeyControlDialog.xaml.cs | 89 ++++++++++--------- Flow.Launcher/Languages/en.xaml | 5 +- .../Resources/Pages/WelcomePage2.xaml | 1 + Flow.Launcher/SettingWindow.xaml | 12 +++ 11 files changed, 201 insertions(+), 56 deletions(-) create mode 100644 Flow.Launcher.Infrastructure/Hotkey/IHotkeySettings.cs create mode 100644 Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs diff --git a/Flow.Launcher.Infrastructure/Hotkey/IHotkeySettings.cs b/Flow.Launcher.Infrastructure/Hotkey/IHotkeySettings.cs new file mode 100644 index 000000000..295a569ee --- /dev/null +++ b/Flow.Launcher.Infrastructure/Hotkey/IHotkeySettings.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace Flow.Launcher.Infrastructure.Hotkey; + +public interface IHotkeySettings +{ + public List RegisteredHotkeys { get; } +} diff --git a/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs b/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs new file mode 100644 index 000000000..7fa7421cd --- /dev/null +++ b/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs @@ -0,0 +1,19 @@ +using System; + +namespace Flow.Launcher.Infrastructure.Hotkey; + +#nullable enable + +public record RegisteredHotkeyData +{ + public HotkeyModel Hotkey { get; } + public string Description { get; } + public Action? RemoveHotkey { get; } + + public RegisteredHotkeyData(string hotkey, string description, Action? removeHotkey = null) + { + Hotkey = new HotkeyModel(hotkey); + Description = description; + RemoveHotkey = removeHotkey; + } +} diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 343e91a85..b890304e8 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -4,13 +4,14 @@ using System.Collections.ObjectModel; using System.Drawing; using System.Text.Json.Serialization; using System.Windows; +using Flow.Launcher.Infrastructure.Hotkey; using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.SharedModels; using Flow.Launcher.ViewModel; namespace Flow.Launcher.Infrastructure.UserSettings { - public class Settings : BaseModel + public class Settings : BaseModel, IHotkeySettings { private string language = "en"; private string _theme = Constant.DefaultTheme; @@ -207,17 +208,17 @@ namespace Flow.Launcher.Infrastructure.UserSettings public double WindowLeft { get; set; } public double WindowTop { get; set; } - + /// /// Custom left position on selected monitor /// public double CustomWindowLeft { get; set; } = 0; - + /// /// Custom top position on selected monitor /// public double CustomWindowTop { get; set; } = 0; - + public int MaxResultsToShow { get; set; } = 5; public int ActivateTimes { get; set; } @@ -229,7 +230,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings [JsonIgnore] public ObservableCollection BuiltinShortcuts { get; set; } = new() { - new BuiltinShortcutModel("{clipboard}", "shortcut_clipboard_description", Clipboard.GetText), + new BuiltinShortcutModel("{clipboard}", "shortcut_clipboard_description", Clipboard.GetText), new BuiltinShortcutModel("{active_explorer_path}", "shortcut_active_explorer_path", FileExplorerHelper.GetActiveExplorerPath) }; @@ -253,7 +254,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings [JsonConverter(typeof(JsonStringEnumConverter))] public SearchWindowScreens SearchWindowScreen { get; set; } = SearchWindowScreens.Cursor; - + [JsonConverter(typeof(JsonStringEnumConverter))] public SearchWindowAligns SearchWindowAlign { get; set; } = SearchWindowAligns.Center; @@ -273,6 +274,78 @@ namespace Flow.Launcher.Infrastructure.UserSettings // This needs to be loaded last by staying at the bottom public PluginsSettings PluginSettings { get; set; } = new PluginsSettings(); + + [JsonIgnore] + public List RegisteredHotkeys + { + get + { + var list = new List + { + 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($"{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 + }; + + if(!string.IsNullOrEmpty(Hotkey)) + list.Add(new(Hotkey, "Open main window", () => Hotkey = "")); // TODO + if(!string.IsNullOrEmpty(PreviewHotkey)) + list.Add(new(PreviewHotkey, "Preview Hotkey", () => PreviewHotkey = "")); // TODO + if(!string.IsNullOrEmpty(AutoCompleteHotkey)) + list.Add(new(AutoCompleteHotkey, "AutoCompleteHotkey", () => AutoCompleteHotkey = "")); // TODO + if(!string.IsNullOrEmpty(AutoCompleteHotkey2)) + list.Add(new(AutoCompleteHotkey2, "AutoCompleteHotkey", () => AutoCompleteHotkey2 = "")); // TODO + if(!string.IsNullOrEmpty(SelectNextItemHotkey)) + list.Add(new(SelectNextItemHotkey, "SelectNextItemHotkey", () => SelectNextItemHotkey = "")); // TODO + if(!string.IsNullOrEmpty(SelectNextItemHotkey2)) + list.Add(new(SelectNextItemHotkey2, "SelectNextItemHotkey", () => SelectNextItemHotkey2 = "")); // TODO + if(!string.IsNullOrEmpty(SelectPrevItemHotkey)) + list.Add(new(SelectPrevItemHotkey, "SelectPrevItemHotkey", () => SelectPrevItemHotkey = "")); // TODO + if(!string.IsNullOrEmpty(SelectPrevItemHotkey2)) + list.Add(new(SelectPrevItemHotkey2, "SelectPrevItemHotkey", () => SelectPrevItemHotkey2 = "")); // TODO + if(!string.IsNullOrEmpty(SettingWindowHotkey)) + list.Add(new(SettingWindowHotkey, "SettingWindowHotkey", () => SettingWindowHotkey = "")); // TODO + if(!string.IsNullOrEmpty(OpenContextMenuHotkey)) + list.Add(new(OpenContextMenuHotkey, "OpenContextMenuHotkey", () => OpenContextMenuHotkey = "")); // TODO + if(!string.IsNullOrEmpty(SelectNextPageHotkey)) + list.Add(new(SelectNextPageHotkey, "SelectNextPageHotkey", () => SelectNextPageHotkey = "")); // TODO + if(!string.IsNullOrEmpty(SelectPrevPageHotkey)) + list.Add(new(SelectPrevPageHotkey, "SelectPrevPageHotkey", () => SelectPrevPageHotkey = "")); // TODO + + foreach (var customPluginHotkey in CustomPluginHotkeys) + { + if (!string.IsNullOrEmpty(customPluginHotkey.Hotkey)) + list.Add(new(customPluginHotkey.Hotkey, "Custom plugin hotkey", () => customPluginHotkey.Hotkey = "")); // TODO + } + + return list; + } + } } public enum LastQueryMode @@ -288,7 +361,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings Light, Dark } - + public enum SearchWindowScreens { RememberLastLaunchLocation, @@ -297,7 +370,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings Primary, Custom } - + public enum SearchWindowAligns { Center, diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml b/Flow.Launcher/CustomQueryHotkeySetting.xaml index de724ea81..068afda15 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml @@ -106,6 +106,7 @@ HorizontalAlignment="Left" VerticalAlignment="Center" HorizontalContentAlignment="Left" + HotkeySettings="{Binding Settings}" DefaultHotkey="" /> (); + Settings.CustomPluginHotkeys ??= new ObservableCollection(); var pluginHotkey = new CustomPluginHotkey { Hotkey = HotkeyControl.CurrentHotkey.ToString(), ActionKeyword = tbAction.Text }; - _settings.CustomPluginHotkeys.Add(pluginHotkey); + Settings.CustomPluginHotkeys.Add(pluginHotkey); HotKeyMapper.SetCustomQueryHotkey(pluginHotkey); } @@ -59,7 +59,7 @@ namespace Flow.Launcher public void UpdateItem(CustomPluginHotkey item) { - updateCustomHotkey = _settings.CustomPluginHotkeys.FirstOrDefault(o => + updateCustomHotkey = Settings.CustomPluginHotkeys.FirstOrDefault(o => o.ActionKeyword == item.ActionKeyword && o.Hotkey == item.Hotkey); if (updateCustomHotkey == null) { diff --git a/Flow.Launcher/HotkeyControl.xaml.cs b/Flow.Launcher/HotkeyControl.xaml.cs index 546b252df..a42bde7c9 100644 --- a/Flow.Launcher/HotkeyControl.xaml.cs +++ b/Flow.Launcher/HotkeyControl.xaml.cs @@ -12,6 +12,17 @@ namespace Flow.Launcher { public partial class HotkeyControl { + public IHotkeySettings HotkeySettings { + get { return (IHotkeySettings)GetValue(HotkeySettingsProperty); } + set { SetValue(HotkeySettingsProperty, value); } + } + + public static readonly DependencyProperty HotkeySettingsProperty = DependencyProperty.Register( + nameof(HotkeySettings), + typeof(IHotkeySettings), + typeof(HotkeyControl), + new PropertyMetadata() + ); public string WindowTitle { get { return (string)GetValue(WindowTitleProperty); } set { SetValue(WindowTitleProperty, value); } @@ -122,7 +133,7 @@ namespace Flow.Launcher HotKeyMapper.RemoveHotkey(Hotkey); } - var dialog = new HotkeyControlDialog(Hotkey, DefaultHotkey, WindowTitle); + var dialog = new HotkeyControlDialog(Hotkey, DefaultHotkey, HotkeySettings, WindowTitle); await dialog.ShowAsync(); switch (dialog.ResultType) { diff --git a/Flow.Launcher/HotkeyControlDialog.xaml b/Flow.Launcher/HotkeyControlDialog.xaml index 1eed711f3..9a5872f4d 100644 --- a/Flow.Launcher/HotkeyControlDialog.xaml +++ b/Flow.Launcher/HotkeyControlDialog.xaml @@ -106,6 +106,7 @@ Grid.Column="1" x:Name="tbMsg" Margin="0,0,0,2" + Padding="0,0,8,0" HorizontalAlignment="Left" FontSize="13" FontWeight="SemiBold" @@ -127,6 +128,15 @@ Margin="10" HorizontalAlignment="Center" Orientation="Horizontal"> +