Flow.Launcher/Flow.Launcher/Helper/HotKeyMapper.cs

330 lines
11 KiB
C#
Raw Normal View History

2025-06-25 12:38:35 +00:00
using System;
2025-06-25 13:39:33 +00:00
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Input;
2025-06-25 12:38:35 +00:00
using ChefKeys;
using CommunityToolkit.Mvvm.DependencyInjection;
2025-06-25 13:39:33 +00:00
using CommunityToolkit.Mvvm.Input;
2025-06-25 12:38:35 +00:00
using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Infrastructure.Hotkey;
using Flow.Launcher.Infrastructure.UserSettings;
2025-06-25 12:38:35 +00:00
using Flow.Launcher.Plugin;
using Flow.Launcher.ViewModel;
using NHotkey;
using NHotkey.Wpf;
namespace Flow.Launcher.Helper;
internal static class HotKeyMapper
{
2025-04-13 09:50:44 +00:00
private static readonly string ClassName = nameof(HotKeyMapper);
private static Settings _settings;
private static MainViewModel _mainViewModel;
2025-06-26 02:31:43 +00:00
private static readonly Dictionary<HotkeyModel, ICommand> _windowHotkeyEvents = new();
2025-02-23 06:37:29 +00:00
internal static void Initialize()
{
2025-02-23 06:37:29 +00:00
_mainViewModel = Ioc.Default.GetRequiredService<MainViewModel>();
_settings = Ioc.Default.GetService<Settings>();
SetHotkey(_settings.Hotkey, OnToggleHotkey);
LoadCustomPluginHotkey();
2025-06-25 12:38:35 +00:00
LoadGlobalPluginHotkey();
2025-06-25 13:39:33 +00:00
LoadWindowPluginHotkey();
}
internal static void OnToggleHotkey(object sender, HotkeyEventArgs args)
{
if (!_mainViewModel.ShouldIgnoreHotkeys())
_mainViewModel.ToggleFlowLauncher();
}
internal static void OnToggleHotkeyWithChefKeys()
{
if (!_mainViewModel.ShouldIgnoreHotkeys())
_mainViewModel.ToggleFlowLauncher();
}
private static void SetHotkey(string hotkeyStr, EventHandler<HotkeyEventArgs> action)
{
var hotkey = new HotkeyModel(hotkeyStr);
SetHotkey(hotkey, action);
}
private static void SetWithChefKeys(string hotkeyStr)
{
try
{
ChefKeysManager.RegisterHotkey(hotkeyStr, hotkeyStr, OnToggleHotkeyWithChefKeys);
ChefKeysManager.Start();
}
catch (Exception e)
{
2025-04-13 09:50:44 +00:00
App.API.LogError(ClassName,
2025-06-25 13:37:02 +00:00
string.Format("Error registering hotkey: {0} \nStackTrace:{1}",
e.Message,
e.StackTrace));
string errorMsg = string.Format(App.API.GetTranslation("registerHotkeyFailed"), hotkeyStr);
string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle");
2025-04-09 13:27:03 +00:00
App.API.ShowMsgBox(errorMsg, errorMsgTitle);
}
}
internal static void SetHotkey(HotkeyModel hotkey, EventHandler<HotkeyEventArgs> action)
{
string hotkeyStr = hotkey.ToString();
try
{
if (hotkeyStr == "LWin" || hotkeyStr == "RWin")
{
SetWithChefKeys(hotkeyStr);
return;
}
HotkeyManager.Current.AddOrReplace(hotkeyStr, hotkey.CharKey, hotkey.ModifierKeys, action);
}
catch (Exception e)
{
2025-04-13 09:50:44 +00:00
App.API.LogError(ClassName,
2025-06-25 13:37:02 +00:00
string.Format("Error registering hotkey {2}: {0} \nStackTrace:{1}",
e.Message,
e.StackTrace,
hotkeyStr));
string errorMsg = string.Format(App.API.GetTranslation("registerHotkeyFailed"), hotkeyStr);
string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle");
2025-01-09 13:30:11 +00:00
App.API.ShowMsgBox(errorMsg, errorMsgTitle);
}
}
internal static void RemoveHotkey(string hotkeyStr)
{
try
{
if (hotkeyStr == "LWin" || hotkeyStr == "RWin")
{
RemoveWithChefKeys(hotkeyStr);
return;
}
if (!string.IsNullOrEmpty(hotkeyStr))
HotkeyManager.Current.Remove(hotkeyStr);
}
catch (Exception e)
{
2025-04-13 09:50:44 +00:00
App.API.LogError(ClassName,
2025-06-25 13:37:02 +00:00
string.Format("Error removing hotkey: {0} \nStackTrace:{1}",
e.Message,
e.StackTrace));
string errorMsg = string.Format(App.API.GetTranslation("unregisterHotkeyFailed"), hotkeyStr);
string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle");
2025-04-09 13:27:03 +00:00
App.API.ShowMsgBox(errorMsg, errorMsgTitle);
}
}
private static void RemoveWithChefKeys(string hotkeyStr)
{
ChefKeysManager.UnregisterHotkey(hotkeyStr);
ChefKeysManager.Stop();
}
internal static void LoadCustomPluginHotkey()
{
if (_settings.CustomPluginHotkeys == null)
return;
foreach (CustomPluginHotkey hotkey in _settings.CustomPluginHotkeys)
{
SetCustomQueryHotkey(hotkey);
}
}
internal static void SetCustomQueryHotkey(CustomPluginHotkey hotkey)
{
SetHotkey(hotkey.Hotkey, (s, e) =>
{
if (_mainViewModel.ShouldIgnoreHotkeys())
return;
App.API.ShowMainWindow();
2025-04-09 13:26:27 +00:00
App.API.ChangeQuery(hotkey.ActionKeyword, true);
});
}
2025-06-25 12:38:35 +00:00
internal static void LoadGlobalPluginHotkey()
{
var pluginHotkeyInfos = PluginManager.GetPluginHotkeyInfo();
foreach (var info in pluginHotkeyInfos)
{
var pluginPair = info.Key;
var hotkeyInfo = info.Value;
var metadata = pluginPair.Metadata;
foreach (var hotkey in hotkeyInfo)
{
if (hotkey.HotkeyType == HotkeyType.Global && hotkey is GlobalPluginHotkey globalHotkey)
{
2025-06-26 01:28:25 +00:00
var hotkeyStr = metadata.PluginHotkeys.Find(h => h.Id == hotkey.Id)?.Hotkey ?? hotkey.DefaultHotkey;
SetGlobalPluginHotkey(globalHotkey, metadata, hotkeyStr);
2025-06-25 12:38:35 +00:00
}
}
}
}
2025-06-26 01:28:25 +00:00
internal static void SetGlobalPluginHotkey(GlobalPluginHotkey globalHotkey, PluginMetadata metadata, string hotkeyStr)
2025-06-25 12:38:35 +00:00
{
2025-06-26 01:28:25 +00:00
var hotkey = new HotkeyModel(hotkeyStr);
SetGlobalPluginHotkey(globalHotkey, metadata, hotkey);
}
internal static void SetGlobalPluginHotkey(GlobalPluginHotkey globalHotkey, PluginMetadata metadata, HotkeyModel hotkey)
{
var hotkeyStr = hotkey.ToString();
SetHotkey(hotkeyStr, (s, e) =>
2025-06-25 12:38:35 +00:00
{
if (_mainViewModel.ShouldIgnoreHotkeys() || metadata.Disabled)
return;
2025-06-26 01:28:25 +00:00
2025-06-25 12:38:35 +00:00
globalHotkey.Action?.Invoke();
});
}
2025-06-25 13:39:33 +00:00
internal static void LoadWindowPluginHotkey()
{
var windowPluginHotkeys = PluginManager.GetWindowPluginHotkeys();
foreach (var hotkey in windowPluginHotkeys)
{
2025-06-26 01:28:25 +00:00
SetWindowHotkey(hotkey.Key, hotkey.Value);
2025-06-25 13:39:33 +00:00
}
}
2025-06-26 01:28:25 +00:00
internal static void SetWindowHotkey(HotkeyModel hotkey, List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeyModels)
2025-06-25 13:39:33 +00:00
{
try
{
2025-06-25 15:15:45 +00:00
if (hotkeyModels.Count == 0) return;
2025-06-25 13:39:33 +00:00
if (Application.Current?.MainWindow is MainWindow window)
{
2025-06-26 02:31:43 +00:00
// Cache the command for the hotkey if it already exists
2025-06-26 01:28:25 +00:00
var keyGesture = hotkey.ToKeyGesture();
2025-06-25 13:39:33 +00:00
var existingBinding = window.InputBindings
.OfType<KeyBinding>()
2025-06-26 01:28:25 +00:00
.FirstOrDefault(kb =>
kb.Gesture is KeyGesture keyGesture1 &&
keyGesture.Key == keyGesture1.Key &&
keyGesture.Modifiers == keyGesture1.Modifiers);
2025-06-25 13:39:33 +00:00
if (existingBinding != null)
{
2025-06-26 02:31:43 +00:00
// If the hotkey exists, remove the old command
if (_windowHotkeyEvents.ContainsKey(hotkey))
{
window.InputBindings.Remove(existingBinding);
}
// If the hotkey does not exist, save the old command
else
{
_windowHotkeyEvents[hotkey] = existingBinding.Command;
}
2025-06-25 13:39:33 +00:00
}
// Create and add the new key binding
2025-06-26 02:31:43 +00:00
var command = BuildCommand(hotkey, hotkeyModels);
2025-06-25 13:39:33 +00:00
var keyBinding = new KeyBinding(command, keyGesture);
window.InputBindings.Add(keyBinding);
}
}
catch (Exception e)
{
App.API.LogError(ClassName,
string.Format("Error registering window hotkey {2}: {0} \nStackTrace:{1}",
e.Message,
e.StackTrace,
2025-06-26 01:28:25 +00:00
hotkey));
string errorMsg = string.Format(App.API.GetTranslation("registerWindowHotkeyFailed"), hotkey);
2025-06-25 13:39:33 +00:00
string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle");
App.API.ShowMsgBox(errorMsg, errorMsgTitle);
}
}
2025-06-26 02:31:43 +00:00
private static ICommand BuildCommand(HotkeyModel hotkey, List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeyModels)
2025-06-25 13:39:33 +00:00
{
return new RelayCommand(() =>
{
2025-06-26 02:31:43 +00:00
if (_windowHotkeyEvents.TryGetValue(hotkey, out var existingCommand))
{
existingCommand.Execute(null);
}
2025-06-25 13:39:33 +00:00
foreach (var hotkeyModel in hotkeyModels)
{
var metadata = hotkeyModel.Metadata;
if (metadata.Disabled)
continue;
var pluginHotkey = hotkeyModel.PluginHotkey;
if (pluginHotkey.Action?.Invoke(_mainViewModel.Results.SelectedItem?.Result) ?? false)
App.API.HideMainWindow();
}
});
}
2025-06-26 01:28:25 +00:00
internal static void RemoveWindowHotkey(HotkeyModel hotkey)
2025-06-25 13:39:33 +00:00
{
try
{
if (Application.Current?.MainWindow is MainWindow window)
{
// Find and remove the key binding with the specified gesture
2025-06-26 01:28:25 +00:00
var keyGesture = hotkey.ToKeyGesture();
2025-06-25 13:39:33 +00:00
var existingBinding = window.InputBindings
.OfType<KeyBinding>()
2025-06-26 01:28:25 +00:00
.FirstOrDefault(kb =>
kb.Gesture is KeyGesture keyGesture1 &&
keyGesture.Key == keyGesture1.Key &&
keyGesture.Modifiers == keyGesture1.Modifiers);
2025-06-25 13:39:33 +00:00
if (existingBinding != null)
{
window.InputBindings.Remove(existingBinding);
}
2025-06-26 02:31:43 +00:00
// Restore the command if it exists
if (_windowHotkeyEvents.TryGetValue(hotkey, out var command))
{
var keyBinding = new KeyBinding(command, keyGesture);
window.InputBindings.Add(keyBinding);
}
2025-06-25 13:39:33 +00:00
}
}
catch (Exception e)
{
App.API.LogError(ClassName,
string.Format("Error removing window hotkey: {0} \nStackTrace:{1}",
e.Message,
e.StackTrace));
2025-06-26 01:28:25 +00:00
string errorMsg = string.Format(App.API.GetTranslation("unregisterWindowHotkeyFailed"), hotkey);
2025-06-25 13:39:33 +00:00
string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle");
App.API.ShowMsgBox(errorMsg, errorMsgTitle);
}
}
internal static bool CheckAvailability(HotkeyModel currentHotkey)
{
try
{
HotkeyManager.Current.AddOrReplace("HotkeyAvailabilityTest", currentHotkey.CharKey, currentHotkey.ModifierKeys, (sender, e) => { });
return true;
}
catch
{
}
finally
{
HotkeyManager.Current.Remove("HotkeyAvailabilityTest");
}
return false;
}
}