mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge 83ee18427e into 24f6c90f72
This commit is contained in:
commit
ad8de0c092
41 changed files with 2764 additions and 567 deletions
|
|
@ -11,6 +11,7 @@ using Flow.Launcher.Core.ExternalPlugins;
|
|||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.DialogJump;
|
||||
using Flow.Launcher.Infrastructure.Hotkey;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
|
|
@ -32,14 +33,22 @@ namespace Flow.Launcher.Core.Plugin
|
|||
private static readonly ConcurrentDictionary<string, PluginPair> _globalPlugins = [];
|
||||
private static readonly ConcurrentDictionary<string, PluginPair> _nonGlobalPlugins = [];
|
||||
|
||||
public static event Action<PluginHotkeyChangedEvent> PluginHotkeyChanged;
|
||||
public static event Action<PluginPair> PluginHotkeyInitialized;
|
||||
|
||||
private static PluginsSettings Settings;
|
||||
private static readonly ConcurrentBag<string> ModifiedPlugins = [];
|
||||
|
||||
private static readonly ConcurrentBag<PluginPair> _contextMenuPlugins = [];
|
||||
private static readonly ConcurrentBag<PluginPair> _homePlugins = [];
|
||||
private static readonly ConcurrentBag<PluginPair> _translationPlugins = [];
|
||||
private static readonly ConcurrentBag<PluginPair> _hotkeyPlugins = [];
|
||||
private static readonly ConcurrentBag<PluginPair> _externalPreviewPlugins = [];
|
||||
|
||||
private static readonly Lock _pluginHotkeyInfoUpdateLock = new();
|
||||
private static readonly ConcurrentDictionary<PluginPair, List<BasePluginHotkey>> _pluginHotkeyInfo = [];
|
||||
private static readonly ConcurrentDictionary<HotkeyModel, ConcurrentBag<(PluginMetadata, SearchWindowPluginHotkey)>> _windowPluginHotkeys = [];
|
||||
|
||||
/// <summary>
|
||||
/// Directories that will hold Flow Launcher plugin directory
|
||||
/// </summary>
|
||||
|
|
@ -303,6 +312,9 @@ namespace Flow.Launcher.Core.Plugin
|
|||
// Add plugin to Dialog Jump plugin list after the plugin is initialized
|
||||
DialogJump.InitializeDialogJumpPlugin(pair);
|
||||
|
||||
// Check and initialize plugin hotkeys after the plugin is initialized
|
||||
CheckPluginHotkeys(pair);
|
||||
|
||||
// Add plugin to lists after the plugin is initialized
|
||||
AddPluginToLists(pair);
|
||||
}));
|
||||
|
|
@ -360,6 +372,22 @@ namespace Flow.Launcher.Core.Plugin
|
|||
_allInitializedPlugins.TryAdd(pair.Metadata.ID, pair);
|
||||
}
|
||||
|
||||
private static void CheckPluginHotkeys(PluginPair pair)
|
||||
{
|
||||
if (pair.Plugin is IPluginHotkey)
|
||||
{
|
||||
InitializePluginHotkeyInfo(pair);
|
||||
// Since settings cannot be changed concurrently, we must use a lock here
|
||||
lock (_pluginHotkeyInfoUpdateLock)
|
||||
{
|
||||
Settings.UpdatePluginHotkeyInfo(GetPluginHotkeyInfo(pair.Metadata.ID));
|
||||
}
|
||||
InitializeWindowPluginHotkey(pair);
|
||||
_hotkeyPlugins.Add(pair);
|
||||
PluginHotkeyInitialized?.Invoke(pair);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Validate & Query Plugins
|
||||
|
|
@ -587,6 +615,11 @@ namespace Flow.Launcher.Core.Plugin
|
|||
return [.. _translationPlugins.Where(p => !PluginModified(p.Metadata.ID))];
|
||||
}
|
||||
|
||||
public static List<PluginPair> GetHotkeyPlugins()
|
||||
{
|
||||
return [.. _hotkeyPlugins.Where(p => !PluginModified(p.Metadata.ID))];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Update Metadata & Get Plugin
|
||||
|
|
@ -792,6 +825,175 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
#endregion
|
||||
|
||||
#region Plugin Hotkey
|
||||
|
||||
private static void InitializePluginHotkeyInfo(PluginPair pair)
|
||||
{
|
||||
var plugin = (IPluginHotkey)pair.Plugin;
|
||||
var hotkeys = plugin.GetPluginHotkeys();
|
||||
_pluginHotkeyInfo.TryAdd(pair, hotkeys);
|
||||
}
|
||||
|
||||
private static void InitializeWindowPluginHotkey(PluginPair pair)
|
||||
{
|
||||
foreach (var info in GetPluginHotkeyInfo(pair.Metadata.ID))
|
||||
{
|
||||
var pluginPair = info.Key;
|
||||
var hotkeyInfo = info.Value;
|
||||
var metadata = pluginPair.Metadata;
|
||||
foreach (var hotkey in hotkeyInfo)
|
||||
{
|
||||
if (hotkey.HotkeyType == HotkeyType.SearchWindow && hotkey is SearchWindowPluginHotkey searchWindowHotkey)
|
||||
{
|
||||
var hotkeySetting = metadata.PluginHotkeys.Find(h => h.Id == hotkey.Id)?.Hotkey ?? hotkey.DefaultHotkey;
|
||||
var hotkeyModel = new HotkeyModel(hotkeySetting);
|
||||
if (!_windowPluginHotkeys.TryGetValue(hotkeyModel, out var list))
|
||||
{
|
||||
list = [];
|
||||
_windowPluginHotkeys[hotkeyModel] = list;
|
||||
}
|
||||
list.Add((pluginPair.Metadata, searchWindowHotkey));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Dictionary<PluginPair, List<BasePluginHotkey>> GetPluginHotkeyInfo(string id = null)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
// Return all plugin hotkey info except those from modified plugins
|
||||
return _pluginHotkeyInfo.Where(p => !PluginModified(p.Key.Metadata.ID))
|
||||
.ToDictionary(p => p.Key, p => p.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return plugin hotkey info for specified plugin id
|
||||
return _pluginHotkeyInfo.Where(p => p.Key.Metadata.ID == id)
|
||||
.ToDictionary(p => p.Key, p => p.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public static Dictionary<HotkeyModel, ConcurrentBag<(PluginMetadata Metadata, SearchWindowPluginHotkey SearchWindowPluginHotkey)>> GetWindowPluginHotkeys(string id = null)
|
||||
{
|
||||
// Here we do not need to check PluginModified since we will check it in hotkey events
|
||||
if (id == null)
|
||||
{
|
||||
// Return all window plugin hotkeys
|
||||
return _windowPluginHotkeys.ToDictionary(p => p.Key, p => p.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return window plugin hotkeys for specified plugin id
|
||||
// If one HotkeyModel is already included by other plugins, it will be removed so that HotkeyMapper will not register this Window hotkey duplicately
|
||||
var windowPluginHotkeys = new Dictionary<HotkeyModel, ConcurrentBag<(PluginMetadata Metadata, SearchWindowPluginHotkey SearchWindowPluginHotkey)>>();
|
||||
foreach (var key in _windowPluginHotkeys)
|
||||
{
|
||||
// Check if all items in the list are from the specified plugin
|
||||
if (key.Value.All(x => x.Item1.ID == id))
|
||||
{
|
||||
// We must use the reference of this ConcurrentBag so that it can be updated in the next
|
||||
windowPluginHotkeys[key.Key] = key.Value;
|
||||
}
|
||||
}
|
||||
return windowPluginHotkeys;
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpdatePluginHotkeyInfoTranslations(PluginPair pair)
|
||||
{
|
||||
var newHotkeys = ((IPluginHotkey)pair.Plugin).GetPluginHotkeys();
|
||||
if (_pluginHotkeyInfo.TryGetValue(pair, out var oldHotkeys))
|
||||
{
|
||||
foreach (var newHotkey in newHotkeys)
|
||||
{
|
||||
if (oldHotkeys.FirstOrDefault(h => h.Id == newHotkey.Id) is BasePluginHotkey pluginHotkey)
|
||||
{
|
||||
pluginHotkey.Name = newHotkey.Name;
|
||||
pluginHotkey.Description = newHotkey.Description;
|
||||
}
|
||||
else
|
||||
{
|
||||
oldHotkeys.Add(newHotkey);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_pluginHotkeyInfo.TryAdd(pair, newHotkeys);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ChangePluginHotkey(PluginMetadata plugin, GlobalPluginHotkey pluginHotkey, HotkeyModel newHotkey)
|
||||
{
|
||||
var oldHotkeyItem = plugin.PluginHotkeys.First(h => h.Id == pluginHotkey.Id);
|
||||
var settingHotkeyItem = Settings.GetPluginSettings(plugin.ID).pluginHotkeys.First(h => h.Id == pluginHotkey.Id);
|
||||
var oldHotkeyStr = settingHotkeyItem.Hotkey;
|
||||
var oldHotkey = new HotkeyModel(oldHotkeyStr);
|
||||
var newHotkeyStr = newHotkey.ToString();
|
||||
|
||||
// Update hotkey in plugin metadata & setting
|
||||
oldHotkeyItem.Hotkey = newHotkeyStr;
|
||||
settingHotkeyItem.Hotkey = newHotkeyStr;
|
||||
|
||||
PluginHotkeyChanged?.Invoke(new PluginHotkeyChangedEvent(oldHotkey, newHotkey, plugin, pluginHotkey));
|
||||
}
|
||||
|
||||
public static void ChangePluginHotkey(PluginMetadata plugin, SearchWindowPluginHotkey pluginHotkey, HotkeyModel newHotkey)
|
||||
{
|
||||
var oldHotkeyItem = plugin.PluginHotkeys.First(h => h.Id == pluginHotkey.Id);
|
||||
var settingHotkeyItem = Settings.GetPluginSettings(plugin.ID).pluginHotkeys.First(h => h.Id == pluginHotkey.Id);
|
||||
var oldHotkeyStr = settingHotkeyItem.Hotkey;
|
||||
var oldHotkey = new HotkeyModel(oldHotkeyStr);
|
||||
var newHotkeyStr = newHotkey.ToString();
|
||||
|
||||
// Update hotkey in plugin metadata & setting
|
||||
oldHotkeyItem.Hotkey = newHotkeyStr;
|
||||
settingHotkeyItem.Hotkey = newHotkeyStr;
|
||||
|
||||
// Update window plugin hotkey dictionary
|
||||
var oldHotkeyModels = _windowPluginHotkeys[oldHotkey];
|
||||
_windowPluginHotkeys[oldHotkey] = [.. oldHotkeyModels.Where(x => x.Item1.ID != plugin.ID || x.Item2.Id != pluginHotkey.Id)];
|
||||
if (_windowPluginHotkeys[oldHotkey].IsEmpty)
|
||||
{
|
||||
_windowPluginHotkeys.TryRemove(oldHotkey, out var _);
|
||||
}
|
||||
|
||||
if (_windowPluginHotkeys.TryGetValue(newHotkey, out var newHotkeyModels))
|
||||
{
|
||||
var newList = newHotkeyModels.ToList();
|
||||
newList.Add((plugin, pluginHotkey));
|
||||
_windowPluginHotkeys[newHotkey] = [.. newList];
|
||||
}
|
||||
else
|
||||
{
|
||||
_windowPluginHotkeys[newHotkey] =
|
||||
[
|
||||
(plugin, pluginHotkey)
|
||||
];
|
||||
}
|
||||
|
||||
PluginHotkeyChanged?.Invoke(new PluginHotkeyChangedEvent(oldHotkey, newHotkey, plugin, pluginHotkey));
|
||||
}
|
||||
|
||||
#region Class
|
||||
|
||||
public class PluginHotkeyChangedEvent(HotkeyModel oldHotkey, HotkeyModel newHotkey,
|
||||
PluginMetadata metadata, BasePluginHotkey pluginHotkey)
|
||||
{
|
||||
public HotkeyModel NewHotkey { get; } = newHotkey;
|
||||
|
||||
public HotkeyModel OldHotkey { get; } = oldHotkey;
|
||||
|
||||
public PluginMetadata Metadata { get; } = metadata;
|
||||
|
||||
public BasePluginHotkey PluginHotkey { get; } = pluginHotkey;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Plugin Install & Uninstall & Update
|
||||
|
||||
#region Private Functions
|
||||
|
|
|
|||
|
|
@ -368,6 +368,11 @@ namespace Flow.Launcher.Core.Resource
|
|||
{
|
||||
p.Metadata.Name = pluginI18N.GetTranslatedPluginTitle();
|
||||
p.Metadata.Description = pluginI18N.GetTranslatedPluginDescription();
|
||||
if (p.Plugin is IPluginHotkey)
|
||||
{
|
||||
// Update plugin hotkey name & description
|
||||
PluginManager.UpdatePluginHotkeyInfoTranslations(p);
|
||||
}
|
||||
pluginI18N.OnCultureInfoChanged(CultureInfo.CurrentCulture);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
@ -385,6 +390,11 @@ namespace Flow.Launcher.Core.Resource
|
|||
{
|
||||
p.Metadata.Name = pluginI18N.GetTranslatedPluginTitle();
|
||||
p.Metadata.Description = pluginI18N.GetTranslatedPluginDescription();
|
||||
if (p.Plugin is IPluginHotkey)
|
||||
{
|
||||
// Update plugin hotkey name & description
|
||||
PluginManager.UpdatePluginHotkeyInfoTranslations(p);
|
||||
}
|
||||
pluginI18N.OnCultureInfoChanged(CultureInfo.CurrentCulture);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Threading;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.DialogJump.Models;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using NHotkey;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.UI.Accessibility;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.DialogJump
|
||||
{
|
||||
|
|
@ -464,10 +464,15 @@ namespace Flow.Launcher.Infrastructure.DialogJump
|
|||
|
||||
#endregion
|
||||
|
||||
#region Hotkey
|
||||
#region Hotkey Command
|
||||
|
||||
public static void OnToggleHotkey(object sender, HotkeyEventArgs args)
|
||||
private static RelayCommand _dialogJumpCommand;
|
||||
public static IRelayCommand DialogJumpCommand => _dialogJumpCommand ??= new RelayCommand(OnToggleHotkey);
|
||||
|
||||
private static void OnToggleHotkey()
|
||||
{
|
||||
if (!_settings.EnableDialogJump) return;
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
|
|
|
|||
|
|
@ -49,6 +49,10 @@ namespace Flow.Launcher.Infrastructure.Hotkey
|
|||
}
|
||||
}
|
||||
|
||||
public readonly bool IsEmpty => CharKey == Key.None && !Alt && !Shift && !Win && !Ctrl;
|
||||
|
||||
public static HotkeyModel Empty => new();
|
||||
|
||||
public HotkeyModel(string hotkeyString)
|
||||
{
|
||||
Parse(hotkeyString);
|
||||
|
|
@ -117,12 +121,22 @@ namespace Flow.Launcher.Infrastructure.Hotkey
|
|||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
public bool Equals(HotkeyModel other)
|
||||
{
|
||||
return CharKey == other.CharKey && ModifierKeys == other.ModifierKeys;
|
||||
}
|
||||
|
||||
public KeyGesture ToKeyGesture()
|
||||
{
|
||||
return new KeyGesture(CharKey, ModifierKeys);
|
||||
}
|
||||
|
||||
public override readonly string ToString()
|
||||
{
|
||||
return string.Join(" + ", EnumerateDisplayKeys());
|
||||
}
|
||||
|
||||
public IEnumerable<string> EnumerateDisplayKeys()
|
||||
public readonly IEnumerable<string> EnumerateDisplayKeys()
|
||||
{
|
||||
if (Ctrl && CharKey is not (Key.LeftCtrl or Key.RightCtrl))
|
||||
{
|
||||
|
|
@ -155,9 +169,9 @@ namespace Flow.Launcher.Infrastructure.Hotkey
|
|||
/// <summary>
|
||||
/// Validate hotkey
|
||||
/// </summary>
|
||||
/// <param name="validateKeyGestrue">Try to validate hotkey as a KeyGesture.</param>
|
||||
/// <param name="validateKeyGesture">Try to validate hotkey as a KeyGesture.</param>
|
||||
/// <returns></returns>
|
||||
public bool Validate(bool validateKeyGestrue = false)
|
||||
public bool Validate(bool validateKeyGesture = false)
|
||||
{
|
||||
switch (CharKey)
|
||||
{
|
||||
|
|
@ -172,7 +186,7 @@ namespace Flow.Launcher.Infrastructure.Hotkey
|
|||
case Key.None:
|
||||
return false;
|
||||
default:
|
||||
if (validateKeyGestrue)
|
||||
if (validateKeyGesture)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.Hotkey;
|
||||
|
||||
|
|
@ -13,5 +13,5 @@ public interface IHotkeySettings
|
|||
/// 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; }
|
||||
public ObservableCollection<RegisteredHotkeyData> RegisteredHotkeys { get; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
using System.Windows.Input;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.Hotkey;
|
||||
|
||||
|
|
@ -11,10 +13,20 @@ namespace Flow.Launcher.Infrastructure.Hotkey;
|
|||
/// </summary>
|
||||
public record RegisteredHotkeyData
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of this hotkey in the context of the application.
|
||||
/// </summary>
|
||||
public RegisteredHotkeyType RegisteredType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of this hotkey.
|
||||
/// </summary>
|
||||
public HotkeyType Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="HotkeyModel"/> representation of this hotkey.
|
||||
/// </summary>
|
||||
public HotkeyModel Hotkey { get; }
|
||||
public HotkeyModel Hotkey { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// String key in the localization dictionary that represents this hotkey. For example, <c>ReloadPluginHotkey</c>,
|
||||
|
|
@ -28,6 +40,16 @@ public record RegisteredHotkeyData
|
|||
/// </summary>
|
||||
public object?[] DescriptionFormatVariables { get; } = Array.Empty<object?>();
|
||||
|
||||
/// <summary>
|
||||
/// Command of this hotkey. If it's <c>null</c>, the hotkey is assumed to be registered by system.
|
||||
/// </summary>
|
||||
public ICommand? Command { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Command parameter of this hotkey.
|
||||
/// </summary>
|
||||
public object? CommandParameter { get; }
|
||||
|
||||
/// <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.
|
||||
|
|
@ -39,6 +61,12 @@ public record RegisteredHotkeyData
|
|||
/// <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="registeredType">
|
||||
/// The type of this hotkey in the context of the application.
|
||||
/// </param>
|
||||
/// <param name="type">
|
||||
/// Whether this hotkey is global or search window specific.
|
||||
/// </param>
|
||||
/// <param name="hotkey">
|
||||
/// The hotkey this class will represent.
|
||||
/// Example values: <c>F1</c>, <c>Ctrl+Shift+Enter</c>
|
||||
|
|
@ -47,14 +75,68 @@ public record RegisteredHotkeyData
|
|||
/// 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="command">
|
||||
/// The command that will be executed when this hotkey is triggered. If it's <c>null</c>, the hotkey is assumed to be registered by system.
|
||||
/// </param>
|
||||
/// <param name="parameter">
|
||||
/// The command parameter that will be passed to the command when this hotkey is triggered. If it's <c>null</c>, no parameter will be passed.
|
||||
/// </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)
|
||||
public RegisteredHotkeyData(
|
||||
RegisteredHotkeyType registeredType, HotkeyType type, string hotkey, string descriptionResourceKey,
|
||||
ICommand? command, object? parameter = null, Action? removeHotkey = null)
|
||||
{
|
||||
RegisteredType = registeredType;
|
||||
Type = type;
|
||||
Hotkey = new HotkeyModel(hotkey);
|
||||
DescriptionResourceKey = descriptionResourceKey;
|
||||
Command = command;
|
||||
CommandParameter = parameter;
|
||||
RemoveHotkey = removeHotkey;
|
||||
}
|
||||
|
||||
/// <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="registeredType">
|
||||
/// The type of this hotkey in the context of the application.
|
||||
/// </param>
|
||||
/// <param name="type">
|
||||
/// Whether this hotkey is global or search window specific.
|
||||
/// </param>
|
||||
/// <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="command">
|
||||
/// The command that will be executed when this hotkey is triggered. If it's <c>null</c>, the hotkey is assumed to be registered by system.
|
||||
/// </param>
|
||||
/// <param name="parameter">
|
||||
/// The command parameter that will be passed to the command when this hotkey is triggered. If it's <c>null</c>, no parameter will be passed.
|
||||
/// </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(
|
||||
RegisteredHotkeyType registeredType, HotkeyType type, HotkeyModel hotkey, string descriptionResourceKey,
|
||||
ICommand? command, object? parameter = null, Action? removeHotkey = null)
|
||||
{
|
||||
RegisteredType = registeredType;
|
||||
Type = type;
|
||||
Hotkey = hotkey;
|
||||
DescriptionResourceKey = descriptionResourceKey;
|
||||
Command = command;
|
||||
CommandParameter = parameter;
|
||||
RemoveHotkey = removeHotkey;
|
||||
}
|
||||
|
||||
|
|
@ -62,6 +144,12 @@ public record RegisteredHotkeyData
|
|||
/// 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="registeredType">
|
||||
/// The type of this hotkey in the context of the application.
|
||||
/// </param>
|
||||
/// <param name="type">
|
||||
/// Whether this hotkey is global or search window specific.
|
||||
/// </param>
|
||||
/// <param name="hotkey">
|
||||
/// The hotkey this class will represent.
|
||||
/// Example values: <c>F1</c>, <c>Ctrl+Shift+Enter</c>
|
||||
|
|
@ -73,17 +161,28 @@ public record RegisteredHotkeyData
|
|||
/// <param name="descriptionFormatVariable">
|
||||
/// The value that will replace <c>{0}</c> in the localized string found via <c>description</c>.
|
||||
/// </param>
|
||||
/// <param name="command">
|
||||
/// The command that will be executed when this hotkey is triggered. If it's <c>null</c>, the hotkey is assumed to be registered by system.
|
||||
/// </param>
|
||||
/// <param name="parameter">
|
||||
/// The command parameter that will be passed to the command when this hotkey is triggered. If it's <c>null</c>, no parameter will be passed.
|
||||
/// </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
|
||||
RegisteredHotkeyType registeredType, HotkeyType type, string hotkey, string descriptionResourceKey, object? descriptionFormatVariable,
|
||||
ICommand? command, object? parameter = null, Action? removeHotkey = null
|
||||
)
|
||||
{
|
||||
RegisteredType = registeredType;
|
||||
Type = type;
|
||||
Hotkey = new HotkeyModel(hotkey);
|
||||
DescriptionResourceKey = descriptionResourceKey;
|
||||
DescriptionFormatVariables = new[] { descriptionFormatVariable };
|
||||
Command = command;
|
||||
CommandParameter = parameter;
|
||||
RemoveHotkey = removeHotkey;
|
||||
}
|
||||
|
||||
|
|
@ -91,6 +190,12 @@ public record RegisteredHotkeyData
|
|||
/// 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="registeredType">
|
||||
/// The type of this hotkey in the context of the application.
|
||||
/// </param>
|
||||
/// <param name="type">
|
||||
/// Whether this hotkey is global or search window specific.
|
||||
/// </param>
|
||||
/// <param name="hotkey">
|
||||
/// The hotkey this class will represent.
|
||||
/// Example values: <c>F1</c>, <c>Ctrl+Shift+Enter</c>
|
||||
|
|
@ -103,17 +208,103 @@ public record RegisteredHotkeyData
|
|||
/// 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="command">
|
||||
/// The command that will be executed when this hotkey is triggered. If it's <c>null</c>, the hotkey is assumed to be registered by system.
|
||||
/// </param>
|
||||
/// <param name="parameter">
|
||||
/// The command parameter that will be passed to the command when this hotkey is triggered. If it's <c>null</c>, no parameter will be passed.
|
||||
/// </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
|
||||
RegisteredHotkeyType registeredType, HotkeyType type, string hotkey, string descriptionResourceKey, object?[] descriptionFormatVariables,
|
||||
ICommand? command, object? parameter = null, Action? removeHotkey = null
|
||||
)
|
||||
{
|
||||
RegisteredType = registeredType;
|
||||
Type = type;
|
||||
Hotkey = new HotkeyModel(hotkey);
|
||||
DescriptionResourceKey = descriptionResourceKey;
|
||||
DescriptionFormatVariables = descriptionFormatVariables;
|
||||
Command = command;
|
||||
CommandParameter = parameter;
|
||||
RemoveHotkey = removeHotkey;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the hotkey for this registered hotkey data.
|
||||
/// </summary>
|
||||
/// <param name="hotkey"></param>
|
||||
public void SetHotkey(HotkeyModel hotkey)
|
||||
{
|
||||
Hotkey = hotkey;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return Hotkey.IsEmpty ? $"{RegisteredType} - None" : $"{RegisteredType} - {Hotkey}";
|
||||
}
|
||||
}
|
||||
|
||||
public enum RegisteredHotkeyType
|
||||
{
|
||||
CtrlShiftEnter,
|
||||
CtrlEnter,
|
||||
AltEnter,
|
||||
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
|
||||
Esc,
|
||||
Reload,
|
||||
SelectFirstResult,
|
||||
SelectLastResult,
|
||||
ReQuery,
|
||||
IncreaseWidth,
|
||||
DecreaseWidth,
|
||||
IncreaseMaxResult,
|
||||
DecreaseMaxResult,
|
||||
ShiftEnter,
|
||||
Enter,
|
||||
ToggleGameMode,
|
||||
CopyFilePath,
|
||||
OpenResultN1,
|
||||
OpenResultN2,
|
||||
OpenResultN3,
|
||||
OpenResultN4,
|
||||
OpenResultN5,
|
||||
OpenResultN6,
|
||||
OpenResultN7,
|
||||
OpenResultN8,
|
||||
OpenResultN9,
|
||||
OpenResultN10,
|
||||
|
||||
Toggle,
|
||||
DialogJump,
|
||||
|
||||
Preview,
|
||||
AutoComplete,
|
||||
AutoComplete2,
|
||||
SelectNextItem,
|
||||
SelectNextItem2,
|
||||
SelectPrevItem,
|
||||
SelectPrevItem2,
|
||||
SettingWindow,
|
||||
OpenHistory,
|
||||
OpenContextMenu,
|
||||
SelectNextPage,
|
||||
SelectPrevPage,
|
||||
CycleHistoryUp,
|
||||
CycleHistoryDown,
|
||||
|
||||
CustomQuery,
|
||||
|
||||
PluginGlobalHotkey,
|
||||
|
||||
PluginWindowHotkey,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,6 +89,116 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update plugin hotkey information in metadata and plugin setting.
|
||||
/// </summary>
|
||||
/// <param name="hotkeyPluginInfo"></param>
|
||||
public void UpdatePluginHotkeyInfo(IDictionary<PluginPair, List<BasePluginHotkey>> hotkeyPluginInfo)
|
||||
{
|
||||
foreach (var info in hotkeyPluginInfo)
|
||||
{
|
||||
var pluginPair = info.Key;
|
||||
var hotkeyInfo = info.Value;
|
||||
var metadata = pluginPair.Metadata;
|
||||
metadata.PluginHotkeys ??= [];
|
||||
metadata.PluginHotkeys.Clear();
|
||||
if (Plugins.TryGetValue(pluginPair.Metadata.ID, out var plugin))
|
||||
{
|
||||
if (plugin.pluginHotkeys == null || plugin.pluginHotkeys.Count == 0)
|
||||
{
|
||||
// If plugin hotkeys does not exist, create a new one and initialize with default values
|
||||
plugin.pluginHotkeys = [];
|
||||
foreach (var hotkey in hotkeyInfo)
|
||||
{
|
||||
plugin.pluginHotkeys.Add(new PluginHotkey
|
||||
{
|
||||
Id = hotkey.Id,
|
||||
DefaultHotkey = hotkey.DefaultHotkey, // hotkey info provides default values
|
||||
Hotkey = hotkey.DefaultHotkey // use default value
|
||||
});
|
||||
metadata.PluginHotkeys.Add(new PluginHotkey
|
||||
{
|
||||
Id = hotkey.Id,
|
||||
DefaultHotkey = hotkey.DefaultHotkey, // hotkey info provides default values
|
||||
Hotkey = hotkey.DefaultHotkey // use default value
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If plugin hotkeys exist, update the existing hotkeys with the new values
|
||||
foreach (var hotkey in hotkeyInfo)
|
||||
{
|
||||
var existingHotkey = plugin.pluginHotkeys.Find(h => h.Id == hotkey.Id);
|
||||
if (existingHotkey != null)
|
||||
{
|
||||
// Update existing hotkey
|
||||
existingHotkey.DefaultHotkey = hotkey.DefaultHotkey; // hotkey info provides default values
|
||||
if (!hotkey.Editable) // If this hotkey is not editable anymore, we need to restore the hotkey
|
||||
{
|
||||
existingHotkey.Hotkey = hotkey.DefaultHotkey;
|
||||
}
|
||||
metadata.PluginHotkeys.Add(new PluginHotkey
|
||||
{
|
||||
Id = hotkey.Id,
|
||||
DefaultHotkey = hotkey.DefaultHotkey, // hotkey info provides default values
|
||||
Hotkey = existingHotkey.Hotkey // use settings value
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add new hotkey if it does not exist
|
||||
plugin.pluginHotkeys.Add(new PluginHotkey
|
||||
{
|
||||
Id = hotkey.Id,
|
||||
DefaultHotkey = hotkey.DefaultHotkey, // hotkey info provides default values
|
||||
Hotkey = hotkey.DefaultHotkey // use default value
|
||||
});
|
||||
metadata.PluginHotkeys.Add(new PluginHotkey
|
||||
{
|
||||
Id = hotkey.Id,
|
||||
DefaultHotkey = hotkey.DefaultHotkey, // hotkey info provides default values
|
||||
Hotkey = hotkey.DefaultHotkey // use default value
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If settings does not exist, create a new one
|
||||
Plugins[metadata.ID] = new Plugin
|
||||
{
|
||||
ID = metadata.ID,
|
||||
Name = metadata.Name,
|
||||
Version = metadata.Version,
|
||||
DefaultActionKeywords = metadata.ActionKeywords, // metadata provides default values
|
||||
ActionKeywords = metadata.ActionKeywords, // use default value
|
||||
Disabled = metadata.Disabled,
|
||||
HomeDisabled = metadata.HomeDisabled,
|
||||
Priority = metadata.Priority,
|
||||
DefaultSearchDelayTime = metadata.SearchDelayTime, // metadata provides default values
|
||||
SearchDelayTime = metadata.SearchDelayTime, // use default value
|
||||
};
|
||||
foreach (var hotkey in hotkeyInfo)
|
||||
{
|
||||
Plugins[metadata.ID].pluginHotkeys.Add(new PluginHotkey
|
||||
{
|
||||
Id = hotkey.Id,
|
||||
DefaultHotkey = hotkey.DefaultHotkey, // hotkey info provides default values
|
||||
Hotkey = hotkey.DefaultHotkey // use default value
|
||||
});
|
||||
metadata.PluginHotkeys.Add(new PluginHotkey
|
||||
{
|
||||
Id = hotkey.Id,
|
||||
DefaultHotkey = hotkey.DefaultHotkey, // hotkey info provides default values
|
||||
Hotkey = hotkey.DefaultHotkey // use default value
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Plugin GetPluginSettings(string id)
|
||||
{
|
||||
if (Plugins.TryGetValue(id, out var plugin))
|
||||
|
|
@ -126,6 +236,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
|
||||
public int? SearchDelayTime { get; set; }
|
||||
|
||||
public List<PluginHotkey> pluginHotkeys { get; set; } = new List<PluginHotkey>();
|
||||
|
||||
/// <summary>
|
||||
/// Used only to save the state of the plugin in settings
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -39,8 +39,6 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
_storage.Save();
|
||||
}
|
||||
|
||||
public string Hotkey { get; set; } = $"{KeyConstant.Alt} + {KeyConstant.Space}";
|
||||
|
||||
private string _openResultModifiers = KeyConstant.Alt;
|
||||
public string OpenResultModifiers
|
||||
{
|
||||
|
|
@ -72,21 +70,230 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
}
|
||||
|
||||
public double WindowSize { get; set; } = 580;
|
||||
public string PreviewHotkey { get; set; } = $"F1";
|
||||
public string AutoCompleteHotkey { get; set; } = $"{KeyConstant.Ctrl} + Tab";
|
||||
public string AutoCompleteHotkey2 { get; set; } = $"";
|
||||
public string SelectNextItemHotkey { get; set; } = $"Tab";
|
||||
public string SelectNextItemHotkey2 { get; set; } = $"";
|
||||
public string SelectPrevItemHotkey { get; set; } = $"Shift + Tab";
|
||||
public string SelectPrevItemHotkey2 { get; set; } = $"";
|
||||
public string SelectNextPageHotkey { get; set; } = $"PageUp";
|
||||
public string SelectPrevPageHotkey { get; set; } = $"PageDown";
|
||||
public string OpenContextMenuHotkey { get; set; } = $"Ctrl+O";
|
||||
public string SettingWindowHotkey { get; set; } = $"Ctrl+I";
|
||||
public string OpenHistoryHotkey { get; set; } = $"Ctrl+H";
|
||||
public string CycleHistoryUpHotkey { get; set; } = $"{KeyConstant.Alt} + Up";
|
||||
public string CycleHistoryDownHotkey { get; set; } = $"{KeyConstant.Alt} + Down";
|
||||
public string DialogJumpHotkey { get; set; } = $"{KeyConstant.Alt} + G";
|
||||
|
||||
private string _hotkey = $"{KeyConstant.Alt} + {KeyConstant.Space}";
|
||||
public string Hotkey
|
||||
{
|
||||
get => _hotkey;
|
||||
set
|
||||
{
|
||||
if (_hotkey != value)
|
||||
{
|
||||
_hotkey = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _previewHotkey = "F1";
|
||||
public string PreviewHotkey
|
||||
{
|
||||
get => _previewHotkey;
|
||||
set
|
||||
{
|
||||
if (_previewHotkey != value)
|
||||
{
|
||||
_previewHotkey = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _autoCompleteHotkey = $"{KeyConstant.Ctrl} + Tab";
|
||||
public string AutoCompleteHotkey
|
||||
{
|
||||
get => _autoCompleteHotkey;
|
||||
set
|
||||
{
|
||||
if (_autoCompleteHotkey != value)
|
||||
{
|
||||
_autoCompleteHotkey = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _autoCompleteHotkey2 = "";
|
||||
public string AutoCompleteHotkey2
|
||||
{
|
||||
get => _autoCompleteHotkey2;
|
||||
set
|
||||
{
|
||||
if (_autoCompleteHotkey2 != value)
|
||||
{
|
||||
_autoCompleteHotkey2 = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _selectNextItemHotkey = "Tab";
|
||||
public string SelectNextItemHotkey
|
||||
{
|
||||
get => _selectNextItemHotkey;
|
||||
set
|
||||
{
|
||||
if (_selectNextItemHotkey != value)
|
||||
{
|
||||
_selectNextItemHotkey = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _selectNextItemHotkey2 = "";
|
||||
public string SelectNextItemHotkey2
|
||||
{
|
||||
get => _selectNextItemHotkey2;
|
||||
set
|
||||
{
|
||||
if (_selectNextItemHotkey2 != value)
|
||||
{
|
||||
_selectNextItemHotkey2 = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _selectPrevItemHotkey = "Shift + Tab";
|
||||
public string SelectPrevItemHotkey
|
||||
{
|
||||
get => _selectPrevItemHotkey;
|
||||
set
|
||||
{
|
||||
if (_selectPrevItemHotkey != value)
|
||||
{
|
||||
_selectPrevItemHotkey = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _selectPrevItemHotkey2 = "";
|
||||
public string SelectPrevItemHotkey2
|
||||
{
|
||||
get => _selectPrevItemHotkey2;
|
||||
set
|
||||
{
|
||||
if (_selectPrevItemHotkey2 != value)
|
||||
{
|
||||
_selectPrevItemHotkey2 = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _selectNextPageHotkey = "PageUp";
|
||||
public string SelectNextPageHotkey
|
||||
{
|
||||
get => _selectNextPageHotkey;
|
||||
set
|
||||
{
|
||||
if (_selectNextPageHotkey != value)
|
||||
{
|
||||
_selectNextPageHotkey = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _selectPrevPageHotkey = "PageDown";
|
||||
public string SelectPrevPageHotkey
|
||||
{
|
||||
get => _selectPrevPageHotkey;
|
||||
set
|
||||
{
|
||||
if (_selectPrevPageHotkey != value)
|
||||
{
|
||||
_selectPrevPageHotkey = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _openContextMenuHotkey = "Ctrl+O";
|
||||
public string OpenContextMenuHotkey
|
||||
{
|
||||
get => _openContextMenuHotkey;
|
||||
set
|
||||
{
|
||||
if (_openContextMenuHotkey != value)
|
||||
{
|
||||
_openContextMenuHotkey = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _settingWindowHotkey = "Ctrl+I";
|
||||
public string SettingWindowHotkey
|
||||
{
|
||||
get => _settingWindowHotkey;
|
||||
set
|
||||
{
|
||||
if (_settingWindowHotkey != value)
|
||||
{
|
||||
_settingWindowHotkey = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _openHistoryHotkey = "Ctrl+H";
|
||||
public string OpenHistoryHotkey
|
||||
{
|
||||
get => _openHistoryHotkey;
|
||||
set
|
||||
{
|
||||
if (_openHistoryHotkey != value)
|
||||
{
|
||||
_openHistoryHotkey = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _cycleHistoryUpHotkey = $"{KeyConstant.Alt} + Up";
|
||||
public string CycleHistoryUpHotkey
|
||||
{
|
||||
get => _cycleHistoryUpHotkey;
|
||||
set
|
||||
{
|
||||
if (_cycleHistoryUpHotkey != value)
|
||||
{
|
||||
_cycleHistoryUpHotkey = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _cycleHistoryDownHotkey = $"{KeyConstant.Alt} + Down";
|
||||
public string CycleHistoryDownHotkey
|
||||
{
|
||||
get => _cycleHistoryDownHotkey;
|
||||
set
|
||||
{
|
||||
if (_cycleHistoryDownHotkey != value)
|
||||
{
|
||||
_cycleHistoryDownHotkey = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _dialogJumpHotkey = $"{KeyConstant.Alt} + G";
|
||||
public string DialogJumpHotkey
|
||||
{
|
||||
get => _dialogJumpHotkey;
|
||||
set
|
||||
{
|
||||
if (_dialogJumpHotkey != value)
|
||||
{
|
||||
_dialogJumpHotkey = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _language = Constant.SystemLanguageCode;
|
||||
public string Language
|
||||
|
|
@ -324,7 +531,19 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
}
|
||||
};
|
||||
|
||||
public bool EnableDialogJump { get; set; } = true;
|
||||
private bool _enableDialogJump = true;
|
||||
public bool EnableDialogJump
|
||||
{
|
||||
get => _enableDialogJump;
|
||||
set
|
||||
{
|
||||
if (_enableDialogJump != value)
|
||||
{
|
||||
_enableDialogJump = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool AutoDialogJump { get; set; } = false;
|
||||
|
||||
|
|
@ -449,9 +668,9 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
|
||||
public int ActivateTimes { get; set; }
|
||||
|
||||
public ObservableCollection<CustomPluginHotkey> CustomPluginHotkeys { get; set; } = new ObservableCollection<CustomPluginHotkey>();
|
||||
public ObservableCollection<CustomPluginHotkey> CustomPluginHotkeys { get; set; } = new();
|
||||
|
||||
public ObservableCollection<CustomShortcutModel> CustomShortcuts { get; set; } = new ObservableCollection<CustomShortcutModel>();
|
||||
public ObservableCollection<CustomShortcutModel> CustomShortcuts { get; set; } = new();
|
||||
|
||||
[JsonIgnore]
|
||||
public ObservableCollection<BaseBuiltinShortcutModel> BuiltinShortcuts { get; set; } = new()
|
||||
|
|
@ -538,97 +757,10 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
public bool WMPInstalled { get; set; } = true;
|
||||
|
||||
// This needs to be loaded last by staying at the bottom
|
||||
public PluginsSettings PluginSettings { get; set; } = new PluginsSettings();
|
||||
public PluginsSettings PluginSettings { get; set; } = new();
|
||||
|
||||
[JsonIgnore]
|
||||
public List<RegisteredHotkeyData> RegisteredHotkeys
|
||||
{
|
||||
get
|
||||
{
|
||||
var list = FixedHotkeys();
|
||||
|
||||
// Customizable hotkeys
|
||||
if (!string.IsNullOrEmpty(Hotkey))
|
||||
list.Add(new(Hotkey, "flowlauncherHotkey", () => Hotkey = ""));
|
||||
if (!string.IsNullOrEmpty(PreviewHotkey))
|
||||
list.Add(new(PreviewHotkey, "previewHotkey", () => PreviewHotkey = ""));
|
||||
if (!string.IsNullOrEmpty(AutoCompleteHotkey))
|
||||
list.Add(new(AutoCompleteHotkey, "autoCompleteHotkey", () => AutoCompleteHotkey = ""));
|
||||
if (!string.IsNullOrEmpty(AutoCompleteHotkey2))
|
||||
list.Add(new(AutoCompleteHotkey2, "autoCompleteHotkey", () => AutoCompleteHotkey2 = ""));
|
||||
if (!string.IsNullOrEmpty(SelectNextItemHotkey))
|
||||
list.Add(new(SelectNextItemHotkey, "SelectNextItemHotkey", () => SelectNextItemHotkey = ""));
|
||||
if (!string.IsNullOrEmpty(SelectNextItemHotkey2))
|
||||
list.Add(new(SelectNextItemHotkey2, "SelectNextItemHotkey", () => SelectNextItemHotkey2 = ""));
|
||||
if (!string.IsNullOrEmpty(SelectPrevItemHotkey))
|
||||
list.Add(new(SelectPrevItemHotkey, "SelectPrevItemHotkey", () => SelectPrevItemHotkey = ""));
|
||||
if (!string.IsNullOrEmpty(SelectPrevItemHotkey2))
|
||||
list.Add(new(SelectPrevItemHotkey2, "SelectPrevItemHotkey", () => SelectPrevItemHotkey2 = ""));
|
||||
if (!string.IsNullOrEmpty(SettingWindowHotkey))
|
||||
list.Add(new(SettingWindowHotkey, "SettingWindowHotkey", () => SettingWindowHotkey = ""));
|
||||
if (!string.IsNullOrEmpty(OpenHistoryHotkey))
|
||||
list.Add(new(OpenHistoryHotkey, "OpenHistoryHotkey", () => OpenHistoryHotkey = ""));
|
||||
if (!string.IsNullOrEmpty(OpenContextMenuHotkey))
|
||||
list.Add(new(OpenContextMenuHotkey, "OpenContextMenuHotkey", () => OpenContextMenuHotkey = ""));
|
||||
if (!string.IsNullOrEmpty(SelectNextPageHotkey))
|
||||
list.Add(new(SelectNextPageHotkey, "SelectNextPageHotkey", () => SelectNextPageHotkey = ""));
|
||||
if (!string.IsNullOrEmpty(SelectPrevPageHotkey))
|
||||
list.Add(new(SelectPrevPageHotkey, "SelectPrevPageHotkey", () => SelectPrevPageHotkey = ""));
|
||||
if (!string.IsNullOrEmpty(CycleHistoryUpHotkey))
|
||||
list.Add(new(CycleHistoryUpHotkey, "CycleHistoryUpHotkey", () => CycleHistoryUpHotkey = ""));
|
||||
if (!string.IsNullOrEmpty(CycleHistoryDownHotkey))
|
||||
list.Add(new(CycleHistoryDownHotkey, "CycleHistoryDownHotkey", () => CycleHistoryDownHotkey = ""));
|
||||
if (!string.IsNullOrEmpty(DialogJumpHotkey))
|
||||
list.Add(new(DialogJumpHotkey, "dialogJumpHotkey", () => DialogJumpHotkey = ""));
|
||||
|
||||
// Custom Query Hotkeys
|
||||
foreach (var customPluginHotkey in CustomPluginHotkeys)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(customPluginHotkey.Hotkey))
|
||||
list.Add(new(customPluginHotkey.Hotkey, "customQueryHotkey", () => customPluginHotkey.Hotkey = ""));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
private List<RegisteredHotkeyData> FixedHotkeys()
|
||||
{
|
||||
return new List<RegisteredHotkeyData>
|
||||
{
|
||||
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+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", "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)
|
||||
};
|
||||
}
|
||||
public ObservableCollection<RegisteredHotkeyData> RegisteredHotkeys { get; } = new();
|
||||
}
|
||||
|
||||
public enum LastQueryMode
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Windows.Input;
|
||||
using System;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Flow.Launcher.Plugin
|
||||
{
|
||||
|
|
@ -6,6 +7,7 @@ namespace Flow.Launcher.Plugin
|
|||
/// Context provided as a parameter when invoking a
|
||||
/// <see cref="Result.Action"/> or <see cref="Result.AsyncAction"/>
|
||||
/// </summary>
|
||||
[Obsolete("ActionContext support is deprecated and will be removed in a future release. Please use IPluginHotkey instead.")]
|
||||
public class ActionContext
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
|||
16
Flow.Launcher.Plugin/Interfaces/IPluginHotkey.cs
Normal file
16
Flow.Launcher.Plugin/Interfaces/IPluginHotkey.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Flow.Launcher.Plugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Represent plugins that support global hotkey or search window hotkey.
|
||||
/// </summary>
|
||||
public interface IPluginHotkey : IFeatures
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the list of plugin hotkeys which will be registered in the settings page.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
List<BasePluginHotkey> GetPluginHotkeys();
|
||||
}
|
||||
}
|
||||
134
Flow.Launcher.Plugin/PluginHotkey.cs
Normal file
134
Flow.Launcher.Plugin/PluginHotkey.cs
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
using System;
|
||||
|
||||
namespace Flow.Launcher.Plugin;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a base plugin hotkey model.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Do not use this class directly. Use <see cref="GlobalPluginHotkey"/> or <see cref="SearchWindowPluginHotkey"/> instead.
|
||||
/// </remarks>
|
||||
public class BasePluginHotkey
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BasePluginHotkey"/> class with the specified hotkey type.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of hotkey (Global or SearchWindow).</param>
|
||||
protected BasePluginHotkey(HotkeyType type)
|
||||
{
|
||||
HotkeyType = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The unique identifier for the hotkey, which is used to identify and rank the hotkey in the settings page.
|
||||
/// </summary>
|
||||
public int Id { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the hotkey, which will be displayed in the settings page.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The description of the hotkey, which will be displayed in the settings page.
|
||||
/// </summary>
|
||||
public string Description { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The glyph information for the hotkey, which will be displayed in the settings page.
|
||||
/// </summary>
|
||||
public GlyphInfo Glyph { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The default hotkey that will be used if the user does not set a custom hotkey.
|
||||
/// </summary>
|
||||
public string DefaultHotkey { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The type of the hotkey, which can be either global or search window specific.
|
||||
/// </summary>
|
||||
public HotkeyType HotkeyType { get; } = HotkeyType.Global;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the hotkey is editable by the user in the settings page.
|
||||
/// </summary>
|
||||
public bool Editable { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to show the hotkey in the settings page.
|
||||
/// </summary>
|
||||
public bool Visible { get; set; } = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a global plugin hotkey model.
|
||||
/// </summary>
|
||||
public class GlobalPluginHotkey : BasePluginHotkey
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GlobalPluginHotkey"/> class.
|
||||
/// </summary>
|
||||
public GlobalPluginHotkey() : base(HotkeyType.Global)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An action that will be executed when the hotkey is triggered.
|
||||
/// </summary>
|
||||
public Action Action { get; set; } = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a plugin hotkey that is specific to the search window.
|
||||
/// </summary>
|
||||
public class SearchWindowPluginHotkey : BasePluginHotkey
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SearchWindowPluginHotkey"/> class.
|
||||
/// </summary>
|
||||
public SearchWindowPluginHotkey() : base(HotkeyType.SearchWindow)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An action that will be executed when the hotkey is triggered and a result is selected.
|
||||
/// </summary>
|
||||
public Func<Result, bool> Action { get; set; } = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the type of hotkey for a plugin.
|
||||
/// </summary>
|
||||
public enum HotkeyType
|
||||
{
|
||||
/// <summary>
|
||||
/// A hotkey that will be triggered globally, regardless of the active window.
|
||||
/// </summary>
|
||||
Global,
|
||||
|
||||
/// <summary>
|
||||
/// A hotkey that will be triggered only when the search window is active.
|
||||
/// </summary>
|
||||
SearchWindow
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a plugin hotkey model which is used to store the hotkey information for a plugin.
|
||||
/// </summary>
|
||||
public class PluginHotkey
|
||||
{
|
||||
/// <summary>
|
||||
/// The unique identifier for the hotkey.
|
||||
/// </summary>
|
||||
public int Id { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The default hotkey that will be used if the user does not set a custom hotkey.
|
||||
/// </summary>
|
||||
public string DefaultHotkey { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The current hotkey that the user has set for the plugin.
|
||||
/// </summary>
|
||||
public string Hotkey { get; set; } = string.Empty;
|
||||
}
|
||||
|
|
@ -157,6 +157,11 @@ namespace Flow.Launcher.Plugin
|
|||
/// </summary>
|
||||
public string PluginCacheDirectoryPath { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of registered plugin hotkeys.
|
||||
/// </summary>
|
||||
public List<PluginHotkey> PluginHotkeys { get; set; } = new List<PluginHotkey>();
|
||||
|
||||
/// <summary>
|
||||
/// Convert <see cref="PluginMetadata"/> to string.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -317,6 +317,12 @@ namespace Flow.Launcher.Plugin
|
|||
/// </remarks>
|
||||
public string QuerySuggestionText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of hotkey IDs that are supported for this result.
|
||||
/// Those hotkeys should be registered by IPluginHotkey interface.
|
||||
/// </summary>
|
||||
public IList<int> HotkeyIds { get; set; } = new List<int>();
|
||||
|
||||
/// <summary>
|
||||
/// Run this result, asynchronously
|
||||
/// </summary>
|
||||
|
|
@ -368,7 +374,8 @@ namespace Flow.Launcher.Plugin
|
|||
AddSelectedCount = AddSelectedCount,
|
||||
RecordKey = RecordKey,
|
||||
ShowBadge = ShowBadge,
|
||||
QuerySuggestionText = QuerySuggestionText
|
||||
QuerySuggestionText = QuerySuggestionText,
|
||||
HotkeyIds = HotkeyIds,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -487,5 +487,46 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given name is a valid file name
|
||||
/// </summary>
|
||||
public static bool IsValidFileName(string name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name)) return false;
|
||||
if (IsReservedName(name)) return false;
|
||||
if (name.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given name is a valid name for a directory, not a path
|
||||
/// </summary>
|
||||
public static bool IsValidDirectoryName(string name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name)) return false;
|
||||
if (IsReservedName(name)) return false;
|
||||
var invalidChars = Path.GetInvalidPathChars().Concat(new[] { '/', '\\' }).ToArray();
|
||||
if (name.IndexOfAny(invalidChars) >= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static readonly string[] ReservedNames = new[] { "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" };
|
||||
|
||||
private static bool IsReservedName(string name)
|
||||
{
|
||||
var nameWithoutExtension = Path.GetFileNameWithoutExtension(name).ToUpperInvariant();
|
||||
if (ReservedNames.Contains(nameWithoutExtension))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,27 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using ChefKeys;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Infrastructure.Hotkey;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Flow.Launcher.Core.Plugin;
|
||||
using Flow.Launcher.Infrastructure.DialogJump;
|
||||
using Flow.Launcher.Infrastructure.Hotkey;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.ViewModel;
|
||||
using NHotkey;
|
||||
using NHotkey.Wpf;
|
||||
|
||||
namespace Flow.Launcher.Helper;
|
||||
|
||||
/// <summary>
|
||||
/// Set Flow Launcher global hotkeys & window hotkeys
|
||||
/// </summary>
|
||||
internal static class HotKeyMapper
|
||||
{
|
||||
private static readonly string ClassName = nameof(HotKeyMapper);
|
||||
|
|
@ -17,48 +29,461 @@ internal static class HotKeyMapper
|
|||
private static Settings _settings;
|
||||
private static MainViewModel _mainViewModel;
|
||||
|
||||
#region Initialization
|
||||
|
||||
internal static void Initialize()
|
||||
{
|
||||
_mainViewModel = Ioc.Default.GetRequiredService<MainViewModel>();
|
||||
_settings = Ioc.Default.GetService<Settings>();
|
||||
|
||||
SetHotkey(_settings.Hotkey, OnToggleHotkey);
|
||||
if (_settings.EnableDialogJump)
|
||||
InitializeActionContextHotkeys();
|
||||
InitializeRegisteredHotkeys();
|
||||
|
||||
_settings.PropertyChanged += Settings_PropertyChanged;
|
||||
_settings.CustomPluginHotkeys.CollectionChanged += CustomPluginHotkeys_CollectionChanged;
|
||||
PluginManager.PluginHotkeyChanged += PluginManager_PluginHotkeyChanged;
|
||||
PluginManager.PluginHotkeyInitialized += PluginManager_PluginHotkeyInitialized;
|
||||
}
|
||||
|
||||
private static void InitializeRegisteredHotkeys()
|
||||
{
|
||||
// Fixed hotkeys & Editable hotkeys
|
||||
var list = new List<RegisteredHotkeyData>
|
||||
{
|
||||
SetHotkey(_settings.DialogJumpHotkey, DialogJump.OnToggleHotkey);
|
||||
// System default window hotkeys
|
||||
// Here the description of Up/Down and Left/Right are swapped - it is intentional
|
||||
new(RegisteredHotkeyType.Up, HotkeyType.SearchWindow, "Up", nameof(Localize.HotkeyLeftRightDesc), null),
|
||||
new(RegisteredHotkeyType.Down, HotkeyType.SearchWindow, "Down", nameof(Localize.HotkeyLeftRightDesc), null),
|
||||
new(RegisteredHotkeyType.Left, HotkeyType.SearchWindow, "Left", nameof(Localize.HotkeyUpDownDesc), null),
|
||||
new(RegisteredHotkeyType.Right, HotkeyType.SearchWindow, "Right", nameof(Localize.HotkeyUpDownDesc), null),
|
||||
|
||||
// Flow Launcher window hotkeys
|
||||
new(RegisteredHotkeyType.Esc, HotkeyType.SearchWindow, "Escape", nameof(Localize.HotkeyESCDesc), _mainViewModel.EscCommand),
|
||||
new(RegisteredHotkeyType.Reload, HotkeyType.SearchWindow, "F5", nameof(Localize.ReloadPluginHotkey), _mainViewModel.ReloadPluginDataCommand),
|
||||
new(RegisteredHotkeyType.SelectFirstResult, HotkeyType.SearchWindow, "Alt+Home", nameof(Localize.HotkeySelectFirstResult), _mainViewModel.SelectFirstResultCommand),
|
||||
new(RegisteredHotkeyType.SelectLastResult, HotkeyType.SearchWindow, "Alt+End", nameof(Localize.HotkeySelectLastResult), _mainViewModel.SelectLastResultCommand),
|
||||
new(RegisteredHotkeyType.ReQuery, HotkeyType.SearchWindow, "Ctrl+R", nameof(Localize.HotkeyRequery), _mainViewModel.ReQueryCommand),
|
||||
new(RegisteredHotkeyType.IncreaseWidth, HotkeyType.SearchWindow, "Ctrl+OemCloseBrackets", nameof(Localize.QuickWidthHotkey), _mainViewModel.IncreaseWidthCommand),
|
||||
new(RegisteredHotkeyType.DecreaseWidth, HotkeyType.SearchWindow, "Ctrl+OemOpenBrackets", nameof(Localize.QuickWidthHotkey), _mainViewModel.DecreaseWidthCommand),
|
||||
new(RegisteredHotkeyType.IncreaseMaxResult, HotkeyType.SearchWindow, "Ctrl+OemPlus", nameof(Localize.QuickHeightHotkey), _mainViewModel.IncreaseMaxResultCommand),
|
||||
new(RegisteredHotkeyType.DecreaseMaxResult, HotkeyType.SearchWindow, "Ctrl+OemMinus", nameof(Localize.QuickHeightHotkey), _mainViewModel.DecreaseMaxResultCommand),
|
||||
new(RegisteredHotkeyType.ShiftEnter, HotkeyType.SearchWindow, "Shift+Enter", nameof(Localize.OpenContextMenuHotkey), _mainViewModel.LoadContextMenuCommand),
|
||||
new(RegisteredHotkeyType.Enter, HotkeyType.SearchWindow, "Enter", nameof(Localize.HotkeyRunDesc), _mainViewModel.OpenResultCommand),
|
||||
new(RegisteredHotkeyType.ToggleGameMode, HotkeyType.SearchWindow, "Ctrl+F12", nameof(Localize.ToggleGameModeHotkey), _mainViewModel.ToggleGameModeCommand),
|
||||
new(RegisteredHotkeyType.CopyFilePath, HotkeyType.SearchWindow, "Ctrl+Shift+C", nameof(Localize.CopyFilePathHotkey), _mainViewModel.CopyAlternativeCommand),
|
||||
|
||||
// Result Modifier Hotkeys
|
||||
new(RegisteredHotkeyType.OpenResultN1, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D1", nameof(Localize.HotkeyOpenResultN), 1, _mainViewModel.OpenResultCommand, "0"),
|
||||
new(RegisteredHotkeyType.OpenResultN2, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D2", nameof(Localize.HotkeyOpenResultN), 2, _mainViewModel.OpenResultCommand, "1"),
|
||||
new(RegisteredHotkeyType.OpenResultN3, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D3", nameof(Localize.HotkeyOpenResultN), 3, _mainViewModel.OpenResultCommand, "2"),
|
||||
new(RegisteredHotkeyType.OpenResultN4, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D4", nameof(Localize.HotkeyOpenResultN), 4, _mainViewModel.OpenResultCommand, "3"),
|
||||
new(RegisteredHotkeyType.OpenResultN5, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D5", nameof(Localize.HotkeyOpenResultN), 5, _mainViewModel.OpenResultCommand, "4"),
|
||||
new(RegisteredHotkeyType.OpenResultN6, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D6", nameof(Localize.HotkeyOpenResultN), 6, _mainViewModel.OpenResultCommand, "5"),
|
||||
new(RegisteredHotkeyType.OpenResultN7, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D7", nameof(Localize.HotkeyOpenResultN), 7, _mainViewModel.OpenResultCommand, "6"),
|
||||
new(RegisteredHotkeyType.OpenResultN8, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D8", nameof(Localize.HotkeyOpenResultN), 8, _mainViewModel.OpenResultCommand, "7"),
|
||||
new(RegisteredHotkeyType.OpenResultN9, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D9", nameof(Localize.HotkeyOpenResultN), 9, _mainViewModel.OpenResultCommand, "8"),
|
||||
new(RegisteredHotkeyType.OpenResultN10, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D0", nameof(Localize.HotkeyOpenResultN), 10, _mainViewModel.OpenResultCommand, "9"),
|
||||
|
||||
// Flow Launcher global hotkeys
|
||||
new(RegisteredHotkeyType.Toggle, HotkeyType.Global, _settings.Hotkey, nameof(Localize.flowlauncherHotkey), _mainViewModel.CheckAndToggleFlowLauncherCommand, null, () => _settings.Hotkey = ""),
|
||||
new(RegisteredHotkeyType.DialogJump, HotkeyType.Global, _settings.DialogJumpHotkey, nameof(Localize.dialogJumpHotkey), DialogJump.DialogJumpCommand, null, () => _settings.DialogJumpHotkey = ""),
|
||||
|
||||
// Flow Launcher window hotkeys
|
||||
new(RegisteredHotkeyType.Preview, HotkeyType.SearchWindow, _settings.PreviewHotkey, nameof(Localize.previewHotkey), _mainViewModel.TogglePreviewCommand, null, () => _settings.PreviewHotkey = ""),
|
||||
new(RegisteredHotkeyType.AutoComplete, HotkeyType.SearchWindow, _settings.AutoCompleteHotkey, nameof(Localize.autoCompleteHotkey), _mainViewModel.AutocompleteQueryCommand, null, () => _settings.AutoCompleteHotkey = ""),
|
||||
new(RegisteredHotkeyType.AutoComplete2, HotkeyType.SearchWindow, _settings.AutoCompleteHotkey2, nameof(Localize.autoCompleteHotkey), _mainViewModel.AutocompleteQueryCommand, null, () => _settings.AutoCompleteHotkey2 = ""),
|
||||
new(RegisteredHotkeyType.SelectNextItem, HotkeyType.SearchWindow, _settings.SelectNextItemHotkey, nameof(Localize.SelectNextItemHotkey), _mainViewModel.SelectNextItemCommand, null, () => _settings.SelectNextItemHotkey = ""),
|
||||
new(RegisteredHotkeyType.SelectNextItem2, HotkeyType.SearchWindow, _settings.SelectNextItemHotkey2, nameof(Localize.SelectNextItemHotkey), _mainViewModel.SelectNextItemCommand, null, () => _settings.SelectNextItemHotkey2 = ""),
|
||||
new(RegisteredHotkeyType.SelectPrevItem, HotkeyType.SearchWindow, _settings.SelectPrevItemHotkey, nameof(Localize.SelectPrevItemHotkey), _mainViewModel.SelectPrevItemCommand, null, () => _settings.SelectPrevItemHotkey = ""),
|
||||
new(RegisteredHotkeyType.SelectPrevItem2, HotkeyType.SearchWindow, _settings.SelectPrevItemHotkey2, nameof(Localize.SelectPrevItemHotkey), _mainViewModel.SelectPrevItemCommand, null, () => _settings.SelectPrevItemHotkey2 = ""),
|
||||
new(RegisteredHotkeyType.SettingWindow, HotkeyType.SearchWindow, _settings.SettingWindowHotkey, nameof(Localize.SettingWindowHotkey), _mainViewModel.OpenSettingCommand, null, () => _settings.SettingWindowHotkey = ""),
|
||||
new(RegisteredHotkeyType.OpenHistory, HotkeyType.SearchWindow, _settings.OpenHistoryHotkey, nameof(Localize.ToggleHistoryHotkey), _mainViewModel.LoadHistoryCommand, null, () => _settings.OpenHistoryHotkey = ""),
|
||||
new(RegisteredHotkeyType.OpenContextMenu, HotkeyType.SearchWindow, _settings.OpenContextMenuHotkey, nameof(Localize.OpenContextMenuHotkey), _mainViewModel.LoadContextMenuCommand, null, () => _settings.OpenContextMenuHotkey = ""),
|
||||
new(RegisteredHotkeyType.SelectNextPage, HotkeyType.SearchWindow, _settings.SelectNextPageHotkey, nameof(Localize.SelectNextPageHotkey), _mainViewModel.SelectNextPageCommand, null, () => _settings.SelectNextPageHotkey = ""),
|
||||
new(RegisteredHotkeyType.SelectPrevPage, HotkeyType.SearchWindow, _settings.SelectPrevPageHotkey, nameof(Localize.SelectPrevPageHotkey), _mainViewModel.SelectPrevPageCommand, null, () => _settings.SelectPrevPageHotkey = ""),
|
||||
new(RegisteredHotkeyType.CycleHistoryUp, HotkeyType.SearchWindow, _settings.CycleHistoryUpHotkey, nameof(Localize.CycleHistoryUpHotkey), _mainViewModel.ReverseHistoryCommand, null, () => _settings.CycleHistoryUpHotkey = ""),
|
||||
new(RegisteredHotkeyType.CycleHistoryDown, HotkeyType.SearchWindow, _settings.CycleHistoryDownHotkey, nameof(Localize.CycleHistoryDownHotkey), _mainViewModel.ForwardHistoryCommand, null, () => _settings.CycleHistoryDownHotkey = "")
|
||||
};
|
||||
|
||||
// Custom query global hotkeys
|
||||
foreach (var customPluginHotkey in _settings.CustomPluginHotkeys)
|
||||
{
|
||||
list.Add(GetRegisteredHotkeyData(customPluginHotkey));
|
||||
}
|
||||
LoadCustomPluginHotkey();
|
||||
|
||||
// Add registered hotkeys & Set them
|
||||
foreach (var hotkey in list)
|
||||
{
|
||||
_settings.RegisteredHotkeys.Add(hotkey);
|
||||
if (hotkey.RegisteredType == RegisteredHotkeyType.DialogJump && !_settings.EnableDialogJump)
|
||||
{
|
||||
// If dialog jump is disabled, do not register the hotkey
|
||||
continue;
|
||||
}
|
||||
SetHotkey(hotkey);
|
||||
}
|
||||
|
||||
App.API.LogDebug(ClassName, $"Initialize {_settings.RegisteredHotkeys.Count} hotkeys:\n[\n\t{string.Join(",\n\t", _settings.RegisteredHotkeys)}\n]");
|
||||
}
|
||||
|
||||
internal static void OnToggleHotkey(object sender, HotkeyEventArgs args)
|
||||
private static void PluginManager_PluginHotkeyInitialized(PluginPair pair)
|
||||
{
|
||||
if (!_mainViewModel.ShouldIgnoreHotkeys())
|
||||
_mainViewModel.ToggleFlowLauncher();
|
||||
var list = new List<RegisteredHotkeyData>();
|
||||
|
||||
// Global plugin hotkeys
|
||||
var pluginHotkeyInfos = PluginManager.GetPluginHotkeyInfo(pair.Metadata.ID);
|
||||
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)
|
||||
{
|
||||
var hotkeyStr = metadata.PluginHotkeys.Find(h => h.Id == hotkey.Id)?.Hotkey ?? hotkey.DefaultHotkey;
|
||||
list.Add(GetRegisteredHotkeyData(new(hotkeyStr), metadata, globalHotkey));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Window plugin hotkeys
|
||||
var windowPluginHotkeys = PluginManager.GetWindowPluginHotkeys(pair.Metadata.ID);
|
||||
foreach (var hotkey in windowPluginHotkeys)
|
||||
{
|
||||
var hotkeyModel = hotkey.Key;
|
||||
var windowHotkeys = hotkey.Value;
|
||||
list.Add(GetRegisteredHotkeyData(hotkeyModel, windowHotkeys));
|
||||
}
|
||||
|
||||
// Add registered hotkeys & Set them
|
||||
foreach (var hotkey in list)
|
||||
{
|
||||
_settings.RegisteredHotkeys.Add(hotkey);
|
||||
SetHotkey(hotkey);
|
||||
}
|
||||
|
||||
App.API.LogDebug(ClassName, $"Initialize {list.Count} hotkeys for {pair.Metadata.Name}:\n[\n\t{string.Join(",\n\t", list)}\n]");
|
||||
}
|
||||
|
||||
internal static void OnToggleHotkeyWithChefKeys()
|
||||
#endregion
|
||||
|
||||
#region Hotkey Change Events
|
||||
|
||||
private static void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (!_mainViewModel.ShouldIgnoreHotkeys())
|
||||
_mainViewModel.ToggleFlowLauncher();
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
// Flow Launcher global hotkeys
|
||||
case nameof(_settings.Hotkey):
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.Toggle, _settings.Hotkey);
|
||||
break;
|
||||
case nameof(_settings.DialogJumpHotkey):
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.DialogJump, _settings.DialogJumpHotkey, _settings.EnableDialogJump);
|
||||
break;
|
||||
case nameof(_settings.EnableDialogJump):
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.DialogJump, _settings.DialogJumpHotkey, _settings.EnableDialogJump);
|
||||
break;
|
||||
|
||||
// Flow Launcher window hotkeys
|
||||
case nameof(_settings.PreviewHotkey):
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.Preview, _settings.PreviewHotkey);
|
||||
break;
|
||||
case nameof(_settings.AutoCompleteHotkey):
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.AutoComplete, _settings.AutoCompleteHotkey);
|
||||
break;
|
||||
case nameof(_settings.AutoCompleteHotkey2):
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.AutoComplete2, _settings.AutoCompleteHotkey2);
|
||||
break;
|
||||
case nameof(_settings.SelectNextItemHotkey):
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.SelectNextItem, _settings.SelectNextItemHotkey);
|
||||
break;
|
||||
case nameof(_settings.SelectNextItemHotkey2):
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.SelectNextItem2, _settings.SelectNextItemHotkey2);
|
||||
break;
|
||||
case nameof(_settings.SelectPrevItemHotkey):
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.SelectPrevItem, _settings.SelectPrevItemHotkey);
|
||||
break;
|
||||
case nameof(_settings.SelectPrevItemHotkey2):
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.SelectPrevItem2, _settings.SelectPrevItemHotkey2);
|
||||
break;
|
||||
case nameof(_settings.SettingWindowHotkey):
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.SettingWindow, _settings.SettingWindowHotkey);
|
||||
break;
|
||||
case nameof(_settings.OpenHistoryHotkey):
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.OpenHistory, _settings.OpenHistoryHotkey);
|
||||
break;
|
||||
case nameof(_settings.OpenContextMenuHotkey):
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.OpenContextMenu, _settings.OpenContextMenuHotkey);
|
||||
break;
|
||||
case nameof(_settings.SelectNextPageHotkey):
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.SelectNextPage, _settings.SelectNextPageHotkey);
|
||||
break;
|
||||
case nameof(_settings.SelectPrevPageHotkey):
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.SelectPrevPage, _settings.SelectPrevPageHotkey);
|
||||
break;
|
||||
case nameof(_settings.CycleHistoryUpHotkey):
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.CycleHistoryUp, _settings.CycleHistoryUpHotkey);
|
||||
break;
|
||||
case nameof(_settings.CycleHistoryDownHotkey):
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.CycleHistoryDown, _settings.CycleHistoryDownHotkey);
|
||||
break;
|
||||
|
||||
// Result Modifier Hotkeys
|
||||
case nameof(_settings.OpenResultModifiers):
|
||||
// Change all result modifier hotkeys
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.OpenResultN1, $"{_settings.OpenResultModifiers}+D1");
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.OpenResultN2, $"{_settings.OpenResultModifiers}+D2");
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.OpenResultN3, $"{_settings.OpenResultModifiers}+D3");
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.OpenResultN4, $"{_settings.OpenResultModifiers}+D4");
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.OpenResultN5, $"{_settings.OpenResultModifiers}+D5");
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.OpenResultN6, $"{_settings.OpenResultModifiers}+D6");
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.OpenResultN7, $"{_settings.OpenResultModifiers}+D7");
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.OpenResultN8, $"{_settings.OpenResultModifiers}+D8");
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.OpenResultN9, $"{_settings.OpenResultModifiers}+D9");
|
||||
ChangeRegisteredHotkey(RegisteredHotkeyType.OpenResultN10, $"{_settings.OpenResultModifiers}+D0");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetHotkey(string hotkeyStr, EventHandler<HotkeyEventArgs> action)
|
||||
private static void CustomPluginHotkeys_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
var hotkey = new HotkeyModel(hotkeyStr);
|
||||
SetHotkey(hotkey, action);
|
||||
switch (e.Action)
|
||||
{
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
foreach (var item in e.NewItems)
|
||||
{
|
||||
if (item is CustomPluginHotkey customPluginHotkey)
|
||||
{
|
||||
var hotkeyData = GetRegisteredHotkeyData(customPluginHotkey);
|
||||
_settings.RegisteredHotkeys.Add(hotkeyData);
|
||||
SetHotkey(hotkeyData);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
foreach (var item in e.OldItems)
|
||||
{
|
||||
if (item is CustomPluginHotkey customPluginHotkey)
|
||||
{
|
||||
var hotkeyData = SearchRegisteredHotkeyData(customPluginHotkey);
|
||||
_settings.RegisteredHotkeys.Remove(hotkeyData);
|
||||
RemoveHotkey(hotkeyData);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Replace:
|
||||
foreach (var item in e.OldItems)
|
||||
{
|
||||
if (item is CustomPluginHotkey customPluginHotkey)
|
||||
{
|
||||
var hotkeyData = SearchRegisteredHotkeyData(customPluginHotkey);
|
||||
_settings.RegisteredHotkeys.Remove(hotkeyData);
|
||||
RemoveHotkey(hotkeyData);
|
||||
}
|
||||
}
|
||||
foreach (var item in e.NewItems)
|
||||
{
|
||||
if (item is CustomPluginHotkey customPluginHotkey)
|
||||
{
|
||||
var hotkeyData = GetRegisteredHotkeyData(customPluginHotkey);
|
||||
_settings.RegisteredHotkeys.Add(hotkeyData);
|
||||
SetHotkey(hotkeyData);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetWithChefKeys(string hotkeyStr)
|
||||
private static void PluginManager_PluginHotkeyChanged(PluginManager.PluginHotkeyChangedEvent e)
|
||||
{
|
||||
var oldHotkey = e.OldHotkey;
|
||||
var newHotkey = e.NewHotkey;
|
||||
var metadata = e.Metadata;
|
||||
var pluginHotkey = e.PluginHotkey;
|
||||
|
||||
if (pluginHotkey is GlobalPluginHotkey globalPluginHotkey)
|
||||
{
|
||||
var hotkeyData = SearchRegisteredHotkeyData(metadata, globalPluginHotkey);
|
||||
RemoveHotkey(hotkeyData);
|
||||
hotkeyData.SetHotkey(newHotkey);
|
||||
SetHotkey(hotkeyData);
|
||||
}
|
||||
else if (pluginHotkey is SearchWindowPluginHotkey)
|
||||
{
|
||||
// Search hotkey & Remove registered hotkey data & Unregister hotkeys
|
||||
var oldHotkeyData = SearchRegisteredHotkeyData(RegisteredHotkeyType.PluginWindowHotkey, oldHotkey);
|
||||
_settings.RegisteredHotkeys.Remove(oldHotkeyData);
|
||||
RemoveHotkey(oldHotkeyData);
|
||||
var newHotkeyData = SearchRegisteredHotkeyData(RegisteredHotkeyType.PluginWindowHotkey, newHotkey);
|
||||
_settings.RegisteredHotkeys.Remove(newHotkeyData);
|
||||
RemoveHotkey(newHotkeyData);
|
||||
|
||||
// Get hotkey data & Add new registered hotkeys & Register hotkeys
|
||||
var windowPluginHotkeys = PluginManager.GetWindowPluginHotkeys();
|
||||
if (windowPluginHotkeys.TryGetValue(oldHotkey, out var oldHotkeyModels))
|
||||
{
|
||||
oldHotkeyData = GetRegisteredHotkeyData(oldHotkey, oldHotkeyModels);
|
||||
_settings.RegisteredHotkeys.Add(oldHotkeyData);
|
||||
SetHotkey(oldHotkeyData);
|
||||
}
|
||||
if (windowPluginHotkeys.TryGetValue(newHotkey, out var newHotkeyModels))
|
||||
{
|
||||
newHotkeyData = GetRegisteredHotkeyData(newHotkey, newHotkeyModels);
|
||||
_settings.RegisteredHotkeys.Add(newHotkeyData);
|
||||
SetHotkey(newHotkeyData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Custom Query Hotkey
|
||||
|
||||
private static RegisteredHotkeyData GetRegisteredHotkeyData(CustomPluginHotkey customPluginHotkey)
|
||||
{
|
||||
return new(RegisteredHotkeyType.CustomQuery, HotkeyType.Global, customPluginHotkey.Hotkey, "customQueryHotkey", CustomQueryHotkeyCommand, customPluginHotkey, () => ClearHotkeyForCustomQueryHotkey(customPluginHotkey));
|
||||
}
|
||||
|
||||
private static RegisteredHotkeyData SearchRegisteredHotkeyData(CustomPluginHotkey customPluginHotkey)
|
||||
{
|
||||
return _settings.RegisteredHotkeys.FirstOrDefault(h =>
|
||||
h.RegisteredType == RegisteredHotkeyType.CustomQuery &&
|
||||
customPluginHotkey.Equals(h.CommandParameter));
|
||||
}
|
||||
|
||||
private static void ClearHotkeyForCustomQueryHotkey(CustomPluginHotkey customPluginHotkey)
|
||||
{
|
||||
// Clear hotkey for custom query hotkey
|
||||
customPluginHotkey.Hotkey = string.Empty;
|
||||
|
||||
// Remove hotkey events
|
||||
var hotkeyData = SearchRegisteredHotkeyData(customPluginHotkey);
|
||||
_settings.RegisteredHotkeys.Remove(hotkeyData);
|
||||
RemoveHotkey(hotkeyData);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Plugin Hotkey
|
||||
|
||||
private static RegisteredHotkeyData GetRegisteredHotkeyData(HotkeyModel hotkey, PluginMetadata metadata, GlobalPluginHotkey pluginHotkey)
|
||||
{
|
||||
Action removeHotkeyAction = pluginHotkey.Editable ?
|
||||
() => PluginManager.ChangePluginHotkey(metadata, pluginHotkey, HotkeyModel.Empty) : null;
|
||||
return new(RegisteredHotkeyType.PluginGlobalHotkey, HotkeyType.Global, hotkey, "pluginHotkey", GlobalPluginHotkeyCommand, new GlobalPluginHotkeyPair(metadata, pluginHotkey), removeHotkeyAction);
|
||||
}
|
||||
|
||||
private static RegisteredHotkeyData GetRegisteredHotkeyData(HotkeyModel hotkey, ConcurrentBag<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> windowHotkeys)
|
||||
{
|
||||
Action removeHotkeysAction = windowHotkeys.All(h => h.PluginHotkey.Editable) ?
|
||||
() =>
|
||||
{
|
||||
foreach (var (metadata, pluginHotkey) in windowHotkeys)
|
||||
{
|
||||
PluginManager.ChangePluginHotkey(metadata, pluginHotkey, HotkeyModel.Empty);
|
||||
}
|
||||
} : null;
|
||||
return new(RegisteredHotkeyType.PluginWindowHotkey, HotkeyType.SearchWindow, hotkey, "pluginHotkey", WindowPluginHotkeyCommand, new WindowPluginHotkeyPair(hotkey, windowHotkeys), removeHotkeysAction);
|
||||
}
|
||||
|
||||
private static RegisteredHotkeyData SearchRegisteredHotkeyData(PluginMetadata metadata, GlobalPluginHotkey globalPluginHotkey)
|
||||
{
|
||||
return _settings.RegisteredHotkeys.FirstOrDefault(h =>
|
||||
h.RegisteredType == RegisteredHotkeyType.PluginGlobalHotkey &&
|
||||
h.CommandParameter is GlobalPluginHotkeyPair pair &&
|
||||
pair.Metadata.ID == metadata.ID &&
|
||||
pair.GlobalPluginHotkey.Id == globalPluginHotkey.Id);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Hotkey Setting
|
||||
|
||||
private static void SetHotkey(RegisteredHotkeyData hotkeyData)
|
||||
{
|
||||
if (hotkeyData is null || // Hotkey data is invalid
|
||||
hotkeyData.Hotkey.IsEmpty || // Hotkey is none
|
||||
hotkeyData.Command is null) // No need to set - it is a system command
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotkeyData.Type == HotkeyType.Global)
|
||||
{
|
||||
SetGlobalHotkey(hotkeyData);
|
||||
}
|
||||
else if (hotkeyData.Type == HotkeyType.SearchWindow)
|
||||
{
|
||||
SetWindowHotkey(hotkeyData);
|
||||
}
|
||||
}
|
||||
|
||||
private static void RemoveHotkey(RegisteredHotkeyData hotkeyData)
|
||||
{
|
||||
if (hotkeyData is null || // Hotkey data is invalid
|
||||
hotkeyData.Hotkey.IsEmpty) // Hotkey is none
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotkeyData.Type == HotkeyType.Global)
|
||||
{
|
||||
RemoveGlobalHotkey(hotkeyData);
|
||||
}
|
||||
else if (hotkeyData.Type == HotkeyType.SearchWindow)
|
||||
{
|
||||
RemoveWindowHotkey(hotkeyData);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetGlobalHotkey(RegisteredHotkeyData hotkeyData)
|
||||
{
|
||||
var hotkey = hotkeyData.Hotkey;
|
||||
var hotkeyStr = hotkey.ToString();
|
||||
var hotkeyCommand = hotkeyData.Command;
|
||||
var hotkeyCommandParameter = hotkeyData.CommandParameter;
|
||||
try
|
||||
{
|
||||
ChefKeysManager.RegisterHotkey(hotkeyStr, hotkeyStr, OnToggleHotkeyWithChefKeys);
|
||||
if (hotkeyStr == "LWin" || hotkeyStr == "RWin")
|
||||
{
|
||||
SetGlobalHotkeyWithChefKeys(hotkeyData);
|
||||
return;
|
||||
}
|
||||
|
||||
HotkeyManager.Current.AddOrReplace(
|
||||
hotkeyStr, hotkey.CharKey, hotkey.ModifierKeys,
|
||||
(s, e) => hotkeyCommand.Execute(hotkeyCommandParameter));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
App.API.LogError(ClassName, $"Error registering hotkey {hotkeyStr}: {e.Message} \nStackTrace:{e.StackTrace}");
|
||||
var errorMsg = string.Format(App.API.GetTranslation("registerHotkeyFailed"), hotkeyStr);
|
||||
var errorMsgTitle = App.API.GetTranslation("MessageBoxTitle");
|
||||
App.API.ShowMsgBox(errorMsg, errorMsgTitle);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetGlobalHotkeyWithChefKeys(RegisteredHotkeyData hotkeyData)
|
||||
{
|
||||
var hotkey = hotkeyData.Hotkey;
|
||||
if (hotkey.IsEmpty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var hotkeyStr = hotkey.ToString();
|
||||
var hotkeyCommand = hotkeyData.Command;
|
||||
var hotkeyCommandParameter = hotkeyData.CommandParameter;
|
||||
try
|
||||
{
|
||||
ChefKeysManager.RegisterHotkey(hotkeyStr, hotkeyStr, () => hotkeyCommand.Execute(hotkeyCommandParameter));
|
||||
ChefKeysManager.Start();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
App.API.LogError(ClassName,
|
||||
string.Format("|HotkeyMapper.SetWithChefKeys|Error registering hotkey: {0} \nStackTrace:{1}",
|
||||
string.Format("Error registering hotkey: {0} \nStackTrace:{1}",
|
||||
e.Message,
|
||||
e.StackTrace));
|
||||
string errorMsg = Localize.registerHotkeyFailed(hotkeyStr);
|
||||
|
|
@ -67,39 +492,60 @@ internal static class HotKeyMapper
|
|||
}
|
||||
}
|
||||
|
||||
internal static void SetHotkey(HotkeyModel hotkey, EventHandler<HotkeyEventArgs> action)
|
||||
private static void SetWindowHotkey(RegisteredHotkeyData hotkeyData)
|
||||
{
|
||||
string hotkeyStr = hotkey.ToString();
|
||||
var hotkey = hotkeyData.Hotkey;
|
||||
var hotkeyCommand = hotkeyData.Command;
|
||||
var hotkeyCommandParameter = hotkeyData.CommandParameter;
|
||||
try
|
||||
{
|
||||
if (hotkeyStr == "LWin" || hotkeyStr == "RWin")
|
||||
if (Application.Current?.MainWindow is MainWindow window)
|
||||
{
|
||||
SetWithChefKeys(hotkeyStr);
|
||||
return;
|
||||
}
|
||||
// Check if the hotkey already exists
|
||||
var keyGesture = hotkey.ToKeyGesture();
|
||||
var existingBinding = window.InputBindings
|
||||
.OfType<KeyBinding>()
|
||||
.FirstOrDefault(kb =>
|
||||
kb.Gesture is KeyGesture keyGesture1 &&
|
||||
keyGesture.Key == keyGesture1.Key &&
|
||||
keyGesture.Modifiers == keyGesture1.Modifiers);
|
||||
if (existingBinding != null)
|
||||
{
|
||||
// If the hotkey is not a hotkey for ActionContext events, throw an exception to avoid duplicates
|
||||
if (!IsActionContextEvent(existingBinding, hotkey))
|
||||
{
|
||||
throw new InvalidOperationException($"Key {hotkey} already exists in window");
|
||||
}
|
||||
}
|
||||
|
||||
HotkeyManager.Current.AddOrReplace(hotkeyStr, hotkey.CharKey, hotkey.ModifierKeys, action);
|
||||
// Add the new hotkey binding
|
||||
var keyBinding = new KeyBinding()
|
||||
{
|
||||
Gesture = keyGesture,
|
||||
Command = hotkeyCommand,
|
||||
CommandParameter = hotkeyCommandParameter
|
||||
};
|
||||
window.InputBindings.Add(keyBinding);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
App.API.LogError(ClassName,
|
||||
string.Format("|HotkeyMapper.SetHotkey|Error registering hotkey {2}: {0} \nStackTrace:{1}",
|
||||
e.Message,
|
||||
e.StackTrace,
|
||||
hotkeyStr));
|
||||
string errorMsg = Localize.registerHotkeyFailed(hotkeyStr);
|
||||
string errorMsgTitle = Localize.MessageBoxTitle();
|
||||
App.API.LogError(ClassName, $"Error registering window hotkey {hotkey}: {e.Message} \nStackTrace:{e.StackTrace}");
|
||||
var errorMsg = Localize.registerHotkeyFailed(hotkey);
|
||||
var errorMsgTitle = Localize.MessageBoxTitle();
|
||||
App.API.ShowMsgBox(errorMsg, errorMsgTitle);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void RemoveHotkey(string hotkeyStr)
|
||||
private static void RemoveGlobalHotkey(RegisteredHotkeyData hotkeyData)
|
||||
{
|
||||
var hotkey = hotkeyData.Hotkey;
|
||||
var hotkeyStr = hotkey.ToString();
|
||||
try
|
||||
{
|
||||
if (hotkeyStr == "LWin" || hotkeyStr == "RWin")
|
||||
{
|
||||
RemoveWithChefKeys(hotkeyStr);
|
||||
RemoveGlobalHotkeyWithChefKeys(hotkeyData);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -108,47 +554,183 @@ internal static class HotKeyMapper
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
App.API.LogError(ClassName,
|
||||
string.Format("|HotkeyMapper.RemoveHotkey|Error removing hotkey: {0} \nStackTrace:{1}",
|
||||
e.Message,
|
||||
e.StackTrace));
|
||||
string errorMsg = Localize.unregisterHotkeyFailed(hotkeyStr);
|
||||
string errorMsgTitle = Localize.MessageBoxTitle();
|
||||
App.API.LogError(ClassName, $"Error removing hotkey: {e.Message} \nStackTrace:{e.StackTrace}");
|
||||
var errorMsg = Localize.unregisterHotkeyFailed(hotkeyStr);
|
||||
var errorMsgTitle = Localize.MessageBoxTitle();
|
||||
App.API.ShowMsgBox(errorMsg, errorMsgTitle);
|
||||
}
|
||||
}
|
||||
|
||||
private static void RemoveWithChefKeys(string hotkeyStr)
|
||||
private static void RemoveGlobalHotkeyWithChefKeys(RegisteredHotkeyData hotkeyData)
|
||||
{
|
||||
ChefKeysManager.UnregisterHotkey(hotkeyStr);
|
||||
ChefKeysManager.Stop();
|
||||
}
|
||||
|
||||
internal static void LoadCustomPluginHotkey()
|
||||
{
|
||||
if (_settings.CustomPluginHotkeys == null)
|
||||
return;
|
||||
|
||||
foreach (CustomPluginHotkey hotkey in _settings.CustomPluginHotkeys)
|
||||
var hotkey = hotkeyData.Hotkey;
|
||||
var hotkeyStr = hotkey.ToString();
|
||||
try
|
||||
{
|
||||
SetCustomQueryHotkey(hotkey);
|
||||
ChefKeysManager.UnregisterHotkey(hotkeyStr);
|
||||
ChefKeysManager.Stop();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
App.API.LogError(ClassName, $"Error removing hotkey: {e.Message} \nStackTrace:{e.StackTrace}");
|
||||
var errorMsg = string.Format(App.API.GetTranslation("unregisterHotkeyFailed"), hotkeyStr);
|
||||
var errorMsgTitle = App.API.GetTranslation("MessageBoxTitle");
|
||||
App.API.ShowMsgBox(errorMsg, errorMsgTitle);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void SetCustomQueryHotkey(CustomPluginHotkey hotkey)
|
||||
private static void RemoveWindowHotkey(RegisteredHotkeyData hotkeyData)
|
||||
{
|
||||
SetHotkey(hotkey.Hotkey, (s, e) =>
|
||||
var hotkey = hotkeyData.Hotkey;
|
||||
try
|
||||
{
|
||||
if (_mainViewModel.ShouldIgnoreHotkeys())
|
||||
return;
|
||||
if (Application.Current?.MainWindow is MainWindow window)
|
||||
{
|
||||
// Remove the key binding
|
||||
var keyGesture = hotkey.ToKeyGesture();
|
||||
var existingBinding = window.InputBindings
|
||||
.OfType<KeyBinding>()
|
||||
.FirstOrDefault(kb =>
|
||||
kb.Gesture is KeyGesture keyGesture1 &&
|
||||
keyGesture.Key == keyGesture1.Key &&
|
||||
keyGesture.Modifiers == keyGesture1.Modifiers);
|
||||
if (existingBinding != null)
|
||||
{
|
||||
window.InputBindings.Remove(existingBinding);
|
||||
}
|
||||
|
||||
App.API.ShowMainWindow();
|
||||
// Make sure to go back to the query results page first since it can cause issues if current page is context menu
|
||||
App.API.BackToQueryResults();
|
||||
App.API.ChangeQuery(hotkey.ActionKeyword, true);
|
||||
});
|
||||
// Restore the key binding for ActionContext events
|
||||
RestoreActionContextEvent(hotkey, keyGesture);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
App.API.LogError(ClassName, $"Error removing window hotkey: {e.Message} \nStackTrace:{e.StackTrace}");
|
||||
var errorMsg = string.Format(App.API.GetTranslation("unregisterHotkeyFailed"), hotkey);
|
||||
var errorMsgTitle = App.API.GetTranslation("MessageBoxTitle");
|
||||
App.API.ShowMsgBox(errorMsg, errorMsgTitle);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Hotkey Changing
|
||||
|
||||
private static void ChangeRegisteredHotkey(RegisteredHotkeyType registeredType, string newHotkeyStr, bool setHotkey = true)
|
||||
{
|
||||
var newHotkey = new HotkeyModel(newHotkeyStr);
|
||||
ChangeRegisteredHotkey(registeredType, newHotkey, setHotkey);
|
||||
}
|
||||
|
||||
private static void ChangeRegisteredHotkey(RegisteredHotkeyType registeredType, HotkeyModel newHotkey, bool setHotkey = true)
|
||||
{
|
||||
// Find the old registered hotkey data item
|
||||
var registeredHotkeyData = _settings.RegisteredHotkeys.FirstOrDefault(h => h.RegisteredType == registeredType);
|
||||
|
||||
// If it is not found, return
|
||||
if (registeredHotkeyData == null) return;
|
||||
|
||||
// Remove the old hotkey
|
||||
RemoveHotkey(registeredHotkeyData);
|
||||
|
||||
// Update the hotkey string
|
||||
registeredHotkeyData.SetHotkey(newHotkey);
|
||||
|
||||
// Set the new hotkey
|
||||
if (setHotkey)
|
||||
{
|
||||
SetHotkey(registeredHotkeyData);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Hotkey Searching
|
||||
|
||||
private static RegisteredHotkeyData SearchRegisteredHotkeyData(RegisteredHotkeyType registeredHotkeyType, HotkeyModel hotkeyModel)
|
||||
{
|
||||
return _settings.RegisteredHotkeys.FirstOrDefault(h =>
|
||||
h.RegisteredType == registeredHotkeyType &&
|
||||
h.Hotkey.Equals(hotkeyModel));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Commands
|
||||
|
||||
private static RelayCommand<CustomPluginHotkey> _customQueryHotkeyCommand;
|
||||
private static IRelayCommand CustomQueryHotkeyCommand => _customQueryHotkeyCommand ??= new RelayCommand<CustomPluginHotkey>(CustomQueryHotkey);
|
||||
|
||||
private static RelayCommand<GlobalPluginHotkeyPair> _globalPluginHotkeyCommand;
|
||||
private static IRelayCommand GlobalPluginHotkeyCommand => _globalPluginHotkeyCommand ??= new RelayCommand<GlobalPluginHotkeyPair>(GlobalPluginHotkey);
|
||||
|
||||
private static RelayCommand<WindowPluginHotkeyPair> _windowPluginHotkeyCommand;
|
||||
private static IRelayCommand WindowPluginHotkeyCommand => _windowPluginHotkeyCommand ??= new RelayCommand<WindowPluginHotkeyPair>(WindowPluginHotkey);
|
||||
|
||||
private static void CustomQueryHotkey(CustomPluginHotkey customPluginHotkey)
|
||||
{
|
||||
if (_mainViewModel.ShouldIgnoreHotkeys())
|
||||
return;
|
||||
|
||||
App.API.ShowMainWindow();
|
||||
// Make sure to go back to the query results page first since it can cause issues if current page is context menu
|
||||
App.API.BackToQueryResults();
|
||||
App.API.ChangeQuery(customPluginHotkey.ActionKeyword, true);
|
||||
}
|
||||
|
||||
private static void GlobalPluginHotkey(GlobalPluginHotkeyPair pair)
|
||||
{
|
||||
var metadata = pair.Metadata;
|
||||
var pluginHotkey = pair.GlobalPluginHotkey;
|
||||
|
||||
if (metadata.Disabled || // Check plugin enabled state
|
||||
App.API.PluginModified(metadata.ID)) // Check plugin modified state
|
||||
return;
|
||||
|
||||
if (_mainViewModel.ShouldIgnoreHotkeys())
|
||||
return;
|
||||
|
||||
pluginHotkey.Action?.Invoke();
|
||||
}
|
||||
|
||||
private static void WindowPluginHotkey(WindowPluginHotkeyPair pair)
|
||||
{
|
||||
// Get selected result
|
||||
var selectedResult = _mainViewModel.GetSelectedResults().SelectedItem?.Result;
|
||||
|
||||
// Check result nullability
|
||||
if (selectedResult != null)
|
||||
{
|
||||
var pluginId = selectedResult.PluginID;
|
||||
foreach (var hotkeyModel in pair.HotkeyModels)
|
||||
{
|
||||
var metadata = hotkeyModel.Metadata;
|
||||
var pluginHotkey = hotkeyModel.PluginHotkey;
|
||||
|
||||
if (metadata.ID != pluginId || // Check plugin ID match
|
||||
metadata.Disabled || // Check plugin enabled state
|
||||
App.API.PluginModified(metadata.ID) || // Check plugin modified state
|
||||
!selectedResult.HotkeyIds.Contains(pluginHotkey.Id) || // Check hotkey supported state
|
||||
pluginHotkey.Action == null) // Check action nullability
|
||||
continue;
|
||||
|
||||
// TODO: Remove return to skip other commands
|
||||
if (pluginHotkey.Action.Invoke(selectedResult))
|
||||
App.API.HideMainWindow();
|
||||
|
||||
// Return after invoking the first matching hotkey action so that we will not invoke action context event
|
||||
return;
|
||||
}
|
||||
|
||||
// When no plugin hotkey action is invoked, invoke the action context event
|
||||
InvokeActionContextEvent(pair.Hotkey);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Check Hotkey
|
||||
|
||||
internal static bool CheckAvailability(HotkeyModel currentHotkey)
|
||||
{
|
||||
try
|
||||
|
|
@ -167,4 +749,103 @@ internal static class HotKeyMapper
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Action Context Hotkey (Obsolete)
|
||||
|
||||
[Obsolete("ActionContext support is deprecated and will be removed in a future release. Please use IPluginHotkey instead.")]
|
||||
private static List<RegisteredHotkeyData> _actionContextRegisteredHotkeys;
|
||||
|
||||
[Obsolete("ActionContext support is deprecated and will be removed in a future release. Please use IPluginHotkey instead.")]
|
||||
private static readonly Dictionary<HotkeyModel, (ICommand Command, object Parameter)> _actionContextHotkeyEvents = new();
|
||||
|
||||
[Obsolete("ActionContext support is deprecated and will be removed in a future release. Please use IPluginHotkey instead.")]
|
||||
private static void InitializeActionContextHotkeys()
|
||||
{
|
||||
// Fixed hotkeys for ActionContext
|
||||
_actionContextRegisteredHotkeys =
|
||||
[
|
||||
new(RegisteredHotkeyType.CtrlShiftEnter, HotkeyType.SearchWindow, "Ctrl+Shift+Enter", nameof(Localize.HotkeyCtrlShiftEnterDesc), _mainViewModel.OpenResultCommand),
|
||||
new(RegisteredHotkeyType.CtrlEnter, HotkeyType.SearchWindow, "Ctrl+Enter", nameof(Localize.OpenContainFolderHotkey), _mainViewModel.OpenResultCommand),
|
||||
new(RegisteredHotkeyType.AltEnter, HotkeyType.SearchWindow, "Alt+Enter", nameof(Localize.HotkeyOpenResult), _mainViewModel.OpenResultCommand),
|
||||
];
|
||||
|
||||
// Register ActionContext hotkeys and they will be cached and restored in _actionContextHotkeyEvents
|
||||
foreach (var hotkey in _actionContextRegisteredHotkeys)
|
||||
{
|
||||
_actionContextHotkeyEvents[hotkey.Hotkey] = (hotkey.Command, hotkey.CommandParameter);
|
||||
SetWindowHotkey(hotkey);
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("ActionContext support is deprecated and will be removed in a future release. Please use IPluginHotkey instead.")]
|
||||
private static bool IsActionContextEvent(KeyBinding existingBinding, HotkeyModel hotkey)
|
||||
{
|
||||
// Check if this hotkey is a hotkey for ActionContext events
|
||||
if (_actionContextHotkeyEvents.TryGetValue(hotkey, out var value) &&
|
||||
value.Command == existingBinding.Command &&
|
||||
value.Parameter == existingBinding.CommandParameter)
|
||||
{
|
||||
// If the hotkey is not for ActionContext events, return false
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[Obsolete("ActionContext support is deprecated and will be removed in a future release. Please use IPluginHotkey instead.")]
|
||||
private static void RestoreActionContextEvent(HotkeyModel hotkey, KeyGesture keyGesture)
|
||||
{
|
||||
// Restore the ActionContext event by adding the key binding back
|
||||
if (_actionContextHotkeyEvents.TryGetValue(hotkey, out var actionContextItem))
|
||||
{
|
||||
if (Application.Current?.MainWindow is MainWindow window)
|
||||
{
|
||||
var keyBinding = new KeyBinding
|
||||
{
|
||||
Gesture = keyGesture,
|
||||
Command = actionContextItem.Command,
|
||||
CommandParameter = actionContextItem.Parameter
|
||||
};
|
||||
window.InputBindings.Add(keyBinding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("ActionContext support is deprecated and will be removed in a future release. Please use IPluginHotkey instead.")]
|
||||
private static void InvokeActionContextEvent(HotkeyModel hotkey)
|
||||
{
|
||||
if (_actionContextHotkeyEvents.TryGetValue(hotkey, out var actionContextItem))
|
||||
{
|
||||
actionContextItem.Command.Execute(actionContextItem.Parameter);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Classes
|
||||
|
||||
private class GlobalPluginHotkeyPair
|
||||
{
|
||||
public PluginMetadata Metadata { get; }
|
||||
|
||||
public GlobalPluginHotkey GlobalPluginHotkey { get; }
|
||||
|
||||
public GlobalPluginHotkeyPair(PluginMetadata metadata, GlobalPluginHotkey globalPluginHotkey)
|
||||
{
|
||||
Metadata = metadata;
|
||||
GlobalPluginHotkey = globalPluginHotkey;
|
||||
}
|
||||
}
|
||||
|
||||
private class WindowPluginHotkeyPair(HotkeyModel hotkey, ConcurrentBag<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeys)
|
||||
{
|
||||
[Obsolete("ActionContext support is deprecated and will be removed in a future release. Please use IPluginHotkey instead.")]
|
||||
public HotkeyModel Hotkey { get; } = hotkey;
|
||||
|
||||
public ConcurrentBag<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> HotkeyModels { get; } = hotkeys;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,6 +112,9 @@ namespace Flow.Launcher
|
|||
SelectNextItemHotkey,
|
||||
SelectNextItemHotkey2,
|
||||
DialogJumpHotkey,
|
||||
// Plugin hotkeys
|
||||
GlobalPluginHotkey,
|
||||
WindowPluginHotkey,
|
||||
}
|
||||
|
||||
// We can initialize settings in static field because it has been constructed in App constuctor
|
||||
|
|
@ -144,6 +147,9 @@ namespace Flow.Launcher
|
|||
HotkeyType.SelectNextItemHotkey => _settings.SelectNextItemHotkey,
|
||||
HotkeyType.SelectNextItemHotkey2 => _settings.SelectNextItemHotkey2,
|
||||
HotkeyType.DialogJumpHotkey => _settings.DialogJumpHotkey,
|
||||
// Plugin hotkeys
|
||||
HotkeyType.GlobalPluginHotkey => hotkey,
|
||||
HotkeyType.WindowPluginHotkey => hotkey,
|
||||
_ => throw new System.NotImplementedException("Hotkey type not set")
|
||||
};
|
||||
}
|
||||
|
|
@ -206,6 +212,17 @@ namespace Flow.Launcher
|
|||
case HotkeyType.DialogJumpHotkey:
|
||||
_settings.DialogJumpHotkey = value;
|
||||
break;
|
||||
// Plugin hotkeys
|
||||
case HotkeyType.GlobalPluginHotkey:
|
||||
// We should not save it to settings here because it is a custom plugin hotkey
|
||||
// and it will be saved in the plugin settings
|
||||
hotkey = value;
|
||||
break;
|
||||
case HotkeyType.WindowPluginHotkey:
|
||||
// We should not save it to settings here because it is a custom plugin hotkey
|
||||
// and it will be saved in the plugin settings
|
||||
hotkey = value;
|
||||
break;
|
||||
default:
|
||||
throw new System.NotImplementedException("Hotkey type not set");
|
||||
}
|
||||
|
|
@ -247,11 +264,6 @@ namespace Flow.Launcher
|
|||
|
||||
private async Task OpenHotkeyDialogAsync()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Hotkey))
|
||||
{
|
||||
HotKeyMapper.RemoveHotkey(Hotkey);
|
||||
}
|
||||
|
||||
var dialog = new HotkeyControlDialog(Hotkey, DefaultHotkey, WindowTitle)
|
||||
{
|
||||
Owner = Window.GetWindow(this)
|
||||
|
|
@ -305,8 +317,6 @@ namespace Flow.Launcher
|
|||
|
||||
public void Delete()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Hotkey))
|
||||
HotKeyMapper.RemoveHotkey(Hotkey);
|
||||
Hotkey = "";
|
||||
SetKeysToDisplay(new HotkeyModel(false, false, false, false, Key.None));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -428,6 +428,7 @@
|
|||
<system:String x:Key="DialogJumpFileResultBehaviourFullPath">Fill full path in file name box</system:String>
|
||||
<system:String x:Key="DialogJumpFileResultBehaviourFullPathOpen">Fill full path in file name box and open</system:String>
|
||||
<system:String x:Key="DialogJumpFileResultBehaviourDirectory">Fill directory in path box</system:String>
|
||||
<system:String x:Key="pluginHotkey">Plugin hotkey</system:String>
|
||||
|
||||
<!-- Setting Proxy -->
|
||||
<system:String x:Key="proxy">HTTP Proxy</system:String>
|
||||
|
|
|
|||
|
|
@ -49,169 +49,6 @@
|
|||
<converters:BoolToIMEStateConverter x:Key="BoolToIMEStateConverter" />
|
||||
<converters:StringToKeyBindingConverter x:Key="StringToKeyBindingConverter" />
|
||||
</Window.Resources>
|
||||
<Window.InputBindings>
|
||||
<KeyBinding Key="Escape" Command="{Binding EscCommand}" />
|
||||
<KeyBinding Key="F5" Command="{Binding ReloadPluginDataCommand}" />
|
||||
<KeyBinding
|
||||
Key="Home"
|
||||
Command="{Binding SelectFirstResultCommand}"
|
||||
Modifiers="Alt" />
|
||||
<KeyBinding
|
||||
Key="End"
|
||||
Command="{Binding SelectLastResultCommand}"
|
||||
Modifiers="Alt" />
|
||||
<KeyBinding
|
||||
Key="R"
|
||||
Command="{Binding ReQueryCommand}"
|
||||
Modifiers="Ctrl" />
|
||||
<KeyBinding
|
||||
Key="OemCloseBrackets"
|
||||
Command="{Binding IncreaseWidthCommand}"
|
||||
Modifiers="Control" />
|
||||
<KeyBinding
|
||||
Key="OemOpenBrackets"
|
||||
Command="{Binding DecreaseWidthCommand}"
|
||||
Modifiers="Control" />
|
||||
<KeyBinding
|
||||
Key="OemPlus"
|
||||
Command="{Binding IncreaseMaxResultCommand}"
|
||||
Modifiers="Control" />
|
||||
<KeyBinding
|
||||
Key="OemMinus"
|
||||
Command="{Binding DecreaseMaxResultCommand}"
|
||||
Modifiers="Control" />
|
||||
<KeyBinding
|
||||
Key="Enter"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
Modifiers="Ctrl+Shift" />
|
||||
<KeyBinding
|
||||
Key="Enter"
|
||||
Command="{Binding LoadContextMenuCommand}"
|
||||
Modifiers="Shift" />
|
||||
<KeyBinding Key="Enter" Command="{Binding OpenResultCommand}" />
|
||||
<KeyBinding
|
||||
Key="Enter"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
Modifiers="Ctrl" />
|
||||
<KeyBinding
|
||||
Key="Enter"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
Modifiers="Alt" />
|
||||
<KeyBinding
|
||||
Key="D1"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
CommandParameter="0"
|
||||
Modifiers="{Binding OpenResultCommandModifiers}" />
|
||||
<KeyBinding
|
||||
Key="D2"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
CommandParameter="1"
|
||||
Modifiers="{Binding OpenResultCommandModifiers}" />
|
||||
<KeyBinding
|
||||
Key="D3"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
CommandParameter="2"
|
||||
Modifiers="{Binding OpenResultCommandModifiers}" />
|
||||
<KeyBinding
|
||||
Key="D4"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
CommandParameter="3"
|
||||
Modifiers="{Binding OpenResultCommandModifiers}" />
|
||||
<KeyBinding
|
||||
Key="D5"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
CommandParameter="4"
|
||||
Modifiers="{Binding OpenResultCommandModifiers}" />
|
||||
<KeyBinding
|
||||
Key="D6"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
CommandParameter="5"
|
||||
Modifiers="{Binding OpenResultCommandModifiers}" />
|
||||
<KeyBinding
|
||||
Key="D7"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
CommandParameter="6"
|
||||
Modifiers="{Binding OpenResultCommandModifiers}" />
|
||||
<KeyBinding
|
||||
Key="D8"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
CommandParameter="7"
|
||||
Modifiers="{Binding OpenResultCommandModifiers}" />
|
||||
<KeyBinding
|
||||
Key="D9"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
CommandParameter="8"
|
||||
Modifiers="{Binding OpenResultCommandModifiers}" />
|
||||
<KeyBinding
|
||||
Key="D0"
|
||||
Command="{Binding OpenResultCommand}"
|
||||
CommandParameter="9"
|
||||
Modifiers="{Binding OpenResultCommandModifiers}" />
|
||||
<KeyBinding
|
||||
Key="F12"
|
||||
Command="{Binding ToggleGameModeCommand}"
|
||||
Modifiers="Ctrl" />
|
||||
<KeyBinding
|
||||
Key="C"
|
||||
Command="{Binding CopyAlternativeCommand}"
|
||||
Modifiers="Ctrl+Shift" />
|
||||
<KeyBinding
|
||||
Key="{Binding PreviewHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='key'}"
|
||||
Command="{Binding TogglePreviewCommand}"
|
||||
Modifiers="{Binding PreviewHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='modifiers'}" />
|
||||
<KeyBinding
|
||||
Key="{Binding AutoCompleteHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='key'}"
|
||||
Command="{Binding AutocompleteQueryCommand}"
|
||||
Modifiers="{Binding AutoCompleteHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='modifiers'}" />
|
||||
<KeyBinding
|
||||
Key="{Binding AutoCompleteHotkey2, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='key'}"
|
||||
Command="{Binding AutocompleteQueryCommand}"
|
||||
Modifiers="{Binding AutoCompleteHotkey2, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='modifiers'}" />
|
||||
<KeyBinding
|
||||
Key="{Binding SelectNextItemHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='key'}"
|
||||
Command="{Binding SelectNextItemCommand}"
|
||||
Modifiers="{Binding SelectNextItemHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='modifiers'}" />
|
||||
<KeyBinding
|
||||
Key="{Binding SelectPrevItemHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='key'}"
|
||||
Command="{Binding SelectPrevItemCommand}"
|
||||
Modifiers="{Binding SelectPrevItemHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='modifiers'}" />
|
||||
<KeyBinding
|
||||
Key="{Binding SelectNextItemHotkey2, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='key'}"
|
||||
Command="{Binding SelectNextItemCommand}"
|
||||
Modifiers="{Binding SelectNextItemHotkey2, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='modifiers'}" />
|
||||
<KeyBinding
|
||||
Key="{Binding SelectPrevItemHotkey2, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='key'}"
|
||||
Command="{Binding SelectPrevItemCommand}"
|
||||
Modifiers="{Binding SelectPrevItemHotkey2, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='modifiers'}" />
|
||||
<KeyBinding
|
||||
Key="{Binding SettingWindowHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='key'}"
|
||||
Command="{Binding OpenSettingCommand}"
|
||||
Modifiers="{Binding SettingWindowHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='modifiers'}" />
|
||||
<KeyBinding
|
||||
Key="{Binding OpenHistoryHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='key'}"
|
||||
Command="{Binding LoadHistoryCommand}"
|
||||
Modifiers="{Binding OpenHistoryHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='modifiers'}" />
|
||||
<KeyBinding
|
||||
Key="{Binding OpenContextMenuHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='key'}"
|
||||
Command="{Binding LoadContextMenuCommand}"
|
||||
Modifiers="{Binding OpenContextMenuHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='modifiers'}" />
|
||||
<KeyBinding
|
||||
Key="{Binding SelectNextPageHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='key'}"
|
||||
Command="{Binding SelectNextPageCommand}"
|
||||
Modifiers="{Binding SelectNextPageHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='modifiers'}" />
|
||||
<KeyBinding
|
||||
Key="{Binding SelectPrevPageHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='key'}"
|
||||
Command="{Binding SelectPrevPageCommand}"
|
||||
Modifiers="{Binding SelectPrevPageHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='modifiers'}" />
|
||||
<KeyBinding
|
||||
Key="{Binding CycleHistoryUpHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='key'}"
|
||||
Command="{Binding ReverseHistoryCommand}"
|
||||
Modifiers="{Binding CycleHistoryUpHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='modifiers'}" />
|
||||
<KeyBinding
|
||||
Key="{Binding CycleHistoryDownHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='key'}"
|
||||
Command="{Binding ForwardHistoryCommand}"
|
||||
Modifiers="{Binding CycleHistoryDownHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='modifiers'}" />
|
||||
</Window.InputBindings>
|
||||
|
||||
<Border MouseDown="OnMouseDown" Style="{DynamicResource WindowBorderStyle}">
|
||||
<StackPanel Orientation="Vertical">
|
||||
|
|
|
|||
|
|
@ -113,13 +113,11 @@
|
|||
Text="{DynamicResource flowlauncherHotkey}" />
|
||||
<flowlauncher:HotkeyControl
|
||||
Margin="0 8 0 0"
|
||||
ChangeHotkey="{Binding SetTogglingHotkeyCommand}"
|
||||
DefaultHotkey="Alt+Space"
|
||||
Type="Hotkey"
|
||||
ValidateKeyGesture="True"
|
||||
WindowTitle="{DynamicResource flowlauncherHotkey}" />
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ui:ScrollViewerEx>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
using System.Windows.Media;
|
||||
using System.Windows.Navigation;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Helper;
|
||||
using Flow.Launcher.Infrastructure.Hotkey;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.ViewModel;
|
||||
|
||||
|
|
@ -27,12 +25,6 @@ namespace Flow.Launcher.Resources.Pages
|
|||
base.OnNavigatedTo(e);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private static void SetTogglingHotkey(HotkeyModel hotkey)
|
||||
{
|
||||
HotKeyMapper.SetHotkey(hotkey, HotKeyMapper.OnToggleHotkey);
|
||||
}
|
||||
|
||||
public Brush PreviewBackground
|
||||
{
|
||||
get => WallpaperPathRetrieval.GetWallpaperBrush();
|
||||
|
|
|
|||
|
|
@ -90,7 +90,9 @@
|
|||
Background="Transparent"
|
||||
BorderThickness="0 0 0 0"
|
||||
Header="{DynamicResource HotkeyRunDesc}">
|
||||
<cc:HotkeyDisplay Keys="ENTER" Type="Small" />
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<cc:HotkeyDisplay Keys="ENTER" Type="Small" />
|
||||
</StackPanel>
|
||||
</ui:SettingsCard>
|
||||
<Border
|
||||
Height="1"
|
||||
|
|
@ -100,7 +102,7 @@
|
|||
<ui:SettingsCard
|
||||
Background="Transparent"
|
||||
BorderThickness="0 0 0 0"
|
||||
Header="{DynamicResource HotkeyShiftEnterDesc}">
|
||||
Header="{DynamicResource OpenContextMenuHotkey}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<cc:HotkeyDisplay Keys="SHIFT+ENTER" Type="Small" />
|
||||
</StackPanel>
|
||||
|
|
@ -113,9 +115,9 @@
|
|||
<ui:SettingsCard
|
||||
Background="Transparent"
|
||||
BorderThickness="0 0 0 0"
|
||||
Header="{DynamicResource HotkeyCtrlEnterDesc}">
|
||||
Header="{DynamicResource ReloadPluginHotkey}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<cc:HotkeyDisplay Keys="CTRL+ENTER" Type="Small" />
|
||||
<cc:HotkeyDisplay Keys="F5" Type="Small" />
|
||||
</StackPanel>
|
||||
</ui:SettingsCard>
|
||||
<Border
|
||||
|
|
@ -126,9 +128,95 @@
|
|||
<ui:SettingsCard
|
||||
Background="Transparent"
|
||||
BorderThickness="0 0 0 0"
|
||||
Header="{DynamicResource HotkeyCtrlShiftEnterDesc}">
|
||||
Header="{DynamicResource HotkeySelectFirstResult}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<cc:HotkeyDisplay Keys="CTRL+SHIFT+ENTER" Type="Small" />
|
||||
<cc:HotkeyDisplay Keys="Alt+Home" Type="Small" />
|
||||
</StackPanel>
|
||||
</ui:SettingsCard>
|
||||
<Border
|
||||
Height="1"
|
||||
Background="{DynamicResource Color03B}"
|
||||
BorderThickness="0" />
|
||||
|
||||
<ui:SettingsCard
|
||||
Background="Transparent"
|
||||
BorderThickness="0 0 0 0"
|
||||
Header="{DynamicResource HotkeySelectLastResult}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<cc:HotkeyDisplay Keys="Alt+End" Type="Small" />
|
||||
</StackPanel>
|
||||
</ui:SettingsCard>
|
||||
<Border
|
||||
Height="1"
|
||||
Background="{DynamicResource Color03B}"
|
||||
BorderThickness="0" />
|
||||
|
||||
<ui:SettingsCard
|
||||
Background="Transparent"
|
||||
BorderThickness="0 0 0 0"
|
||||
Header="{DynamicResource HotkeyRequery}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<cc:HotkeyDisplay Keys="Ctrl+R" Type="Small" />
|
||||
</StackPanel>
|
||||
</ui:SettingsCard>
|
||||
<Border
|
||||
Height="1"
|
||||
Background="{DynamicResource Color03B}"
|
||||
BorderThickness="0" />
|
||||
|
||||
<ui:SettingsCard
|
||||
Background="Transparent"
|
||||
BorderThickness="0 0 0 0"
|
||||
Header="{DynamicResource ToggleGameModeHotkey}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<cc:HotkeyDisplay Keys="Ctrl+F12" Type="Small" />
|
||||
</StackPanel>
|
||||
</ui:SettingsCard>
|
||||
<Border
|
||||
Height="1"
|
||||
Background="{DynamicResource Color03B}"
|
||||
BorderThickness="0" />
|
||||
|
||||
<ui:SettingsCard
|
||||
Background="Transparent"
|
||||
BorderThickness="0 0 0 0"
|
||||
Header="{DynamicResource CopyFilePathHotkey}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<cc:HotkeyDisplay Keys="Ctrl+Shift+C" Type="Small" />
|
||||
</StackPanel>
|
||||
</ui:SettingsCard>
|
||||
<Border
|
||||
Height="1"
|
||||
Background="{DynamicResource Color03B}"
|
||||
BorderThickness="0" />
|
||||
|
||||
<ui:SettingsCard
|
||||
Background="Transparent"
|
||||
BorderThickness="0 0 0 0"
|
||||
Header="{DynamicResource QuickWidthHotkey}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<cc:HotkeyDisplay Keys="Ctrl+[" Type="Small" />
|
||||
<cc:HotkeyDisplay
|
||||
Margin="4 0 0 0"
|
||||
Keys="Ctrl+]"
|
||||
Type="Small" />
|
||||
</StackPanel>
|
||||
</ui:SettingsCard>
|
||||
<Border
|
||||
Height="1"
|
||||
Background="{DynamicResource Color03B}"
|
||||
BorderThickness="0" />
|
||||
|
||||
<ui:SettingsCard
|
||||
Background="Transparent"
|
||||
BorderThickness="0 0 0 0"
|
||||
Header="{DynamicResource QuickHeightHotkey}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<cc:HotkeyDisplay Keys="Ctrl+Minus" Type="Small" />
|
||||
<cc:HotkeyDisplay
|
||||
Margin="4 0 0 0"
|
||||
Keys="Ctrl+Plus"
|
||||
Type="Small" />
|
||||
</StackPanel>
|
||||
</ui:SettingsCard>
|
||||
</StackPanel>
|
||||
|
|
|
|||
|
|
@ -158,14 +158,6 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
|
|||
{
|
||||
Settings.EnableDialogJump = value;
|
||||
DialogJump.SetupDialogJump(value);
|
||||
if (Settings.EnableDialogJump)
|
||||
{
|
||||
HotKeyMapper.SetHotkey(new(Settings.DialogJumpHotkey), DialogJump.OnToggleHotkey);
|
||||
}
|
||||
else
|
||||
{
|
||||
HotKeyMapper.RemoveHotkey(Settings.DialogJumpHotkey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
using System.Linq;
|
||||
using System.Windows;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Flow.Launcher.Helper;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Hotkey;
|
||||
using Flow.Launcher.Infrastructure.DialogJump;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
|
|
@ -29,21 +26,6 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel
|
|||
Settings = settings;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void SetTogglingHotkey(HotkeyModel hotkey)
|
||||
{
|
||||
HotKeyMapper.SetHotkey(hotkey, HotKeyMapper.OnToggleHotkey);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void SetDialogJumpHotkey(HotkeyModel hotkey)
|
||||
{
|
||||
if (Settings.EnableDialogJump)
|
||||
{
|
||||
HotKeyMapper.SetHotkey(hotkey, DialogJump.OnToggleHotkey);
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void CustomHotkeyDelete()
|
||||
{
|
||||
|
|
@ -63,7 +45,6 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel
|
|||
if (result is MessageBoxResult.Yes)
|
||||
{
|
||||
Settings.CustomPluginHotkeys.Remove(item);
|
||||
HotKeyMapper.RemoveHotkey(item.Hotkey);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -92,8 +73,6 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel
|
|||
if (index >= 0 && index < Settings.CustomPluginHotkeys.Count)
|
||||
{
|
||||
Settings.CustomPluginHotkeys[index] = new CustomPluginHotkey(window.Hotkey, window.ActionKeyword);
|
||||
HotKeyMapper.RemoveHotkey(settingItem.Hotkey); // remove origin hotkey
|
||||
HotKeyMapper.SetCustomQueryHotkey(Settings.CustomPluginHotkeys[index]); // set new hotkey
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -105,7 +84,6 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel
|
|||
{
|
||||
var customHotkey = new CustomPluginHotkey(window.Hotkey, window.ActionKeyword);
|
||||
Settings.CustomPluginHotkeys.Add(customHotkey);
|
||||
HotKeyMapper.SetCustomQueryHotkey(customHotkey); // set new hotkey
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@
|
|||
</ui:SettingsCard.HeaderIcon>
|
||||
|
||||
<flowlauncher:HotkeyControl
|
||||
ChangeHotkey="{Binding SetTogglingHotkeyCommand}"
|
||||
DefaultHotkey="Alt+Space"
|
||||
Type="Hotkey"
|
||||
ValidateKeyGesture="True"
|
||||
|
|
@ -89,7 +88,6 @@
|
|||
</ui:SettingsCard.HeaderIcon>
|
||||
|
||||
<flowlauncher:HotkeyControl
|
||||
ChangeHotkey="{Binding SetDialogJumpHotkeyCommand}"
|
||||
DefaultHotkey="Alt+G"
|
||||
Type="DialogJumpHotkey"
|
||||
ValidateKeyGesture="False"
|
||||
|
|
@ -105,36 +103,40 @@
|
|||
</ui:SettingsExpander.HeaderIcon>
|
||||
|
||||
<ui:SettingsExpander.Items>
|
||||
<ui:SettingsCard Header="{DynamicResource OpenContainFolderHotkey}">
|
||||
<ui:SettingsCard Header="{DynamicResource HotkeyUpDownDesc}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
<ui:FontIcon Glyph="" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
|
||||
<cc:HotkeyDisplay Keys="Ctrl+Enter" />
|
||||
<cc:HotkeyDisplay Keys="←+→" />
|
||||
</ui:SettingsCard>
|
||||
<ui:SettingsCard Header="{DynamicResource RunAsAdminHotkey}">
|
||||
<ui:SettingsCard Header="{DynamicResource HotkeyLeftRightDesc}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
<ui:FontIcon Glyph="" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
|
||||
<cc:HotkeyDisplay Keys="Ctrl+Shift+Enter" />
|
||||
<cc:HotkeyDisplay Keys="↑+↓" />
|
||||
</ui:SettingsCard>
|
||||
<ui:SettingsCard Header="{DynamicResource ToggleHistoryHotkey}">
|
||||
<ui:SettingsCard Header="{DynamicResource HotkeyESCDesc}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
<ui:FontIcon Glyph="" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
|
||||
<flowlauncher:HotkeyControl
|
||||
DefaultHotkey="Ctrl+H"
|
||||
Type="OpenHistoryHotkey"
|
||||
ValidateKeyGesture="False" />
|
||||
<cc:HotkeyDisplay Keys="ESC" />
|
||||
</ui:SettingsCard>
|
||||
<ui:SettingsCard Header="{DynamicResource CopyFilePathHotkey}">
|
||||
<ui:SettingsCard Header="{DynamicResource HotkeyRunDesc}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
<ui:FontIcon Glyph="" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
|
||||
<cc:HotkeyDisplay Keys="Ctrl+Shift+C" />
|
||||
<cc:HotkeyDisplay Keys="ENTER" />
|
||||
</ui:SettingsCard>
|
||||
<ui:SettingsCard Header="{DynamicResource OpenContextMenuHotkey}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
|
||||
<cc:HotkeyDisplay Keys="Shift+Enter" />
|
||||
</ui:SettingsCard>
|
||||
<ui:SettingsCard Header="{DynamicResource OpenContextMenuHotkey}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
|
|
@ -146,19 +148,15 @@
|
|||
Type="OpenContextMenuHotkey"
|
||||
ValidateKeyGesture="False" />
|
||||
</ui:SettingsCard>
|
||||
<ui:SettingsCard Header="{DynamicResource OpenContextMenuHotkey}">
|
||||
<ui:SettingsCard Header="{DynamicResource ToggleHistoryHotkey}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
<ui:FontIcon Glyph="" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
|
||||
<cc:HotkeyDisplay Keys="Shift+Enter" />
|
||||
</ui:SettingsCard>
|
||||
<ui:SettingsCard Header="{DynamicResource OpenNativeContextMenuHotkey}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
|
||||
<cc:HotkeyDisplay Keys="Alt+Enter" />
|
||||
<flowlauncher:HotkeyControl
|
||||
DefaultHotkey="Ctrl+H"
|
||||
Type="OpenHistoryHotkey"
|
||||
ValidateKeyGesture="False" />
|
||||
</ui:SettingsCard>
|
||||
<ui:SettingsCard Header="{DynamicResource SettingWindowHotkey}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
|
|
@ -170,19 +168,26 @@
|
|||
Type="SettingWindowHotkey"
|
||||
ValidateKeyGesture="False" />
|
||||
</ui:SettingsCard>
|
||||
<ui:SettingsCard Header="{DynamicResource ToggleGameModeHotkey}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
|
||||
<cc:HotkeyDisplay Keys="Ctrl+F12" />
|
||||
</ui:SettingsCard>
|
||||
<ui:SettingsCard Header="{DynamicResource RequeryHotkey}">
|
||||
<ui:SettingsCard Description="{DynamicResource ReloadPluginHotkeyToolTip}" Header="{DynamicResource ReloadPluginHotkey}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
|
||||
<cc:HotkeyDisplay Keys="Ctrl+R" />
|
||||
<cc:HotkeyDisplay Keys="F5" />
|
||||
</ui:SettingsCard>
|
||||
<ui:SettingsCard Header="{DynamicResource HotkeySelectFirstResult}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
|
||||
<cc:HotkeyDisplay Keys="Alt+Home" />
|
||||
</ui:SettingsCard>
|
||||
<ui:SettingsCard Header="{DynamicResource HotkeySelectLastResult}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
|
||||
<cc:HotkeyDisplay Keys="Alt+End" />
|
||||
</ui:SettingsCard>
|
||||
<ui:SettingsCard Header="{DynamicResource CycleHistoryUpHotkey}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
|
|
@ -204,13 +209,6 @@
|
|||
Type="CycleHistoryDownHotkey"
|
||||
ValidateKeyGesture="False" />
|
||||
</ui:SettingsCard>
|
||||
<ui:SettingsCard Description="{DynamicResource ReloadPluginHotkeyToolTip}" Header="{DynamicResource ReloadPluginHotkey}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
|
||||
<cc:HotkeyDisplay Keys="F5" />
|
||||
</ui:SettingsCard>
|
||||
<ui:SettingsCard Header="{DynamicResource SelectPrevPageHotkey}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
|
|
@ -231,7 +229,27 @@
|
|||
Type="SelectNextPageHotkey"
|
||||
ValidateKeyGesture="False" />
|
||||
</ui:SettingsCard>
|
||||
<ui:SettingsCard Header="{DynamicResource RequeryHotkey}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
|
||||
<cc:HotkeyDisplay Keys="Ctrl+R" />
|
||||
</ui:SettingsCard>
|
||||
<ui:SettingsCard Header="{DynamicResource ToggleGameModeHotkey}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
|
||||
<cc:HotkeyDisplay Keys="Ctrl+F12" />
|
||||
</ui:SettingsCard>
|
||||
<ui:SettingsCard Header="{DynamicResource CopyFilePathHotkey}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
|
||||
<cc:HotkeyDisplay Keys="Ctrl+Shift+C" />
|
||||
</ui:SettingsCard>
|
||||
<ui:SettingsCard Header="{DynamicResource QuickWidthHotkey}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
|
|
@ -248,8 +266,8 @@
|
|||
</ui:SettingsCard.HeaderIcon>
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<cc:HotkeyDisplay Keys="Ctrl+Plus" />
|
||||
<cc:HotkeyDisplay Margin="4 0 0 0" Keys="Ctrl+Minus" />
|
||||
<cc:HotkeyDisplay Keys="Ctrl+Minus" />
|
||||
<cc:HotkeyDisplay Margin="4 0 0 0" Keys="Ctrl+Plus" />
|
||||
</StackPanel>
|
||||
</ui:SettingsCard>
|
||||
</ui:SettingsExpander.Items>
|
||||
|
|
@ -318,6 +336,12 @@
|
|||
</ui:SettingsExpander.Items>
|
||||
</ui:SettingsExpander>
|
||||
|
||||
<StackPanel
|
||||
x:Name="PluginHotkeySettings"
|
||||
Margin="0 10 0 0"
|
||||
Loaded="PluginHotkeySettings_Loaded"
|
||||
Orientation="Vertical" />
|
||||
|
||||
<ui:SettingsExpander Margin="0 20 0 0" Header="{DynamicResource customQueryHotkey}">
|
||||
<ui:SettingsExpander.HeaderIcon>
|
||||
<ui:FontIcon Glyph="" />
|
||||
|
|
|
|||
|
|
@ -1,7 +1,19 @@
|
|||
using System.Windows.Navigation;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Navigation;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Flow.Launcher.Core.Plugin;
|
||||
using Flow.Launcher.Infrastructure.Hotkey;
|
||||
using Flow.Launcher.Infrastructure.Image;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Resources.Controls;
|
||||
using Flow.Launcher.SettingPages.ViewModels;
|
||||
using Flow.Launcher.ViewModel;
|
||||
using iNKORE.UI.WPF.Modern.Controls;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Flow.Launcher.SettingPages.Views;
|
||||
|
||||
|
|
@ -28,4 +40,129 @@ public partial class SettingsPaneHotkey
|
|||
}
|
||||
base.OnNavigatedTo(e);
|
||||
}
|
||||
|
||||
private void PluginHotkeySettings_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var pluginHotkeyInfos = PluginManager.GetPluginHotkeyInfo();
|
||||
foreach (var info in pluginHotkeyInfos)
|
||||
{
|
||||
var pluginPair = info.Key;
|
||||
var hotkeyInfo = info.Value;
|
||||
var metadata = pluginPair.Metadata;
|
||||
|
||||
// Skip this plugin if all hotkeys are invisible
|
||||
var allHotkeyInvisible = hotkeyInfo.All(h => !h.Visible);
|
||||
if (allHotkeyInvisible) continue;
|
||||
|
||||
var excard = new SettingsExpander()
|
||||
{
|
||||
Header = metadata.Name + " " + Localize.hotkeys(),
|
||||
Margin = new Thickness(0, 4, 0, 0),
|
||||
HeaderIcon = new Image() { Source = ImageLoader.LoadingImage },
|
||||
Tag = metadata
|
||||
};
|
||||
|
||||
var sortedHotkeyInfo = hotkeyInfo.OrderBy(h => h.Id).ToList();
|
||||
foreach (var hotkey in sortedHotkeyInfo)
|
||||
{
|
||||
// Skip invisible hotkeys
|
||||
if (!hotkey.Visible) continue;
|
||||
|
||||
var card = new SettingsCard()
|
||||
{
|
||||
Header = hotkey.Name,
|
||||
Description = hotkey.Description,
|
||||
HeaderIcon = new FontIcon() { Glyph = hotkey.Glyph.Glyph }
|
||||
};
|
||||
var hotkeySetting = metadata.PluginHotkeys.Find(h => h.Id == hotkey.Id)?.Hotkey ?? hotkey.DefaultHotkey;
|
||||
if (hotkey.Editable)
|
||||
{
|
||||
var hotkeyControl = new HotkeyControl
|
||||
{
|
||||
Type = hotkey.HotkeyType == HotkeyType.Global ?
|
||||
HotkeyControl.HotkeyType.GlobalPluginHotkey :
|
||||
HotkeyControl.HotkeyType.WindowPluginHotkey,
|
||||
DefaultHotkey = hotkey.DefaultHotkey,
|
||||
ValidateKeyGesture = false,
|
||||
Hotkey = hotkeySetting,
|
||||
ChangeHotkey = new RelayCommand<HotkeyModel>((h) => ChangePluginHotkey(metadata, hotkey, h))
|
||||
};
|
||||
card.Content = hotkeyControl;
|
||||
}
|
||||
else
|
||||
{
|
||||
var hotkeyDisplay = new HotkeyDisplay
|
||||
{
|
||||
Keys = hotkeySetting
|
||||
};
|
||||
card.Content = hotkeyDisplay;
|
||||
}
|
||||
excard.Items.Add(card);
|
||||
}
|
||||
PluginHotkeySettings.Children.Add(excard);
|
||||
}
|
||||
|
||||
// Load plugin icons into SettingsExpander asynchronously
|
||||
_ = LoadPluginIconsAsync();
|
||||
}
|
||||
|
||||
private static void ChangePluginHotkey(PluginMetadata metadata, BasePluginHotkey pluginHotkey, HotkeyModel newHotkey)
|
||||
{
|
||||
if (pluginHotkey is GlobalPluginHotkey globalPluginHotkey)
|
||||
{
|
||||
PluginManager.ChangePluginHotkey(metadata, globalPluginHotkey, newHotkey);
|
||||
}
|
||||
else if (pluginHotkey is SearchWindowPluginHotkey windowPluginHotkey)
|
||||
{
|
||||
PluginManager.ChangePluginHotkey(metadata, windowPluginHotkey, newHotkey);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadPluginIconsAsync()
|
||||
{
|
||||
// Snapshot list to avoid collection modification issues
|
||||
var expanders = PluginHotkeySettings.Children
|
||||
.OfType<SettingsExpander>()
|
||||
.Where(e => e.Tag is PluginMetadata m && !string.IsNullOrEmpty(m.IcoPath))
|
||||
.ToList();
|
||||
|
||||
// Fire all loads concurrently
|
||||
var tasks = expanders.Select(async expander =>
|
||||
{
|
||||
if (expander.Tag is not PluginMetadata metadata) return;
|
||||
try
|
||||
{
|
||||
var iconSource = await App.API.LoadImageAsync(metadata.IcoPath);
|
||||
if (iconSource == null) return;
|
||||
|
||||
// Marshal back to UI thread if needed
|
||||
if (!Dispatcher.CheckAccess())
|
||||
{
|
||||
await Dispatcher.InvokeAsync(() => ApplyIcon(expander, iconSource));
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplyIcon(expander, iconSource);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Swallow exceptions to avoid impacting UI; optionally log if logging infra exists
|
||||
}
|
||||
});
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
private static void ApplyIcon(SettingsExpander expander, ImageSource iconSource)
|
||||
{
|
||||
if (expander.HeaderIcon is Image img)
|
||||
{
|
||||
img.Source = iconSource;
|
||||
}
|
||||
else
|
||||
{
|
||||
expander.HeaderIcon = new Image { Source = iconSource };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -585,6 +585,13 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
#region BasicCommands
|
||||
|
||||
[RelayCommand]
|
||||
private void CheckAndToggleFlowLauncher()
|
||||
{
|
||||
if (!ShouldIgnoreHotkeys())
|
||||
ToggleFlowLauncher();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void OpenSetting()
|
||||
{
|
||||
|
|
@ -1874,6 +1881,11 @@ namespace Flow.Launcher.ViewModel
|
|||
return selected;
|
||||
}
|
||||
|
||||
internal ResultsViewModel GetSelectedResults()
|
||||
{
|
||||
return SelectedResults;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Hotkey
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ using Flow.Launcher.Plugin.Explorer.Search;
|
|||
using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks;
|
||||
using Flow.Launcher.Plugin.Explorer.Helper;
|
||||
using Flow.Launcher.Plugin.Explorer.ViewModels;
|
||||
using Flow.Launcher.Plugin.Explorer.Views;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Explorer
|
||||
{
|
||||
|
|
@ -177,6 +179,35 @@ namespace Flow.Launcher.Plugin.Explorer
|
|||
IcoPath = icoPath,
|
||||
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\uf12b")
|
||||
});
|
||||
contextMenus.Add(new Result
|
||||
{
|
||||
Title = Localize.plugin_explorer_rename_a_file(),
|
||||
SubTitle = Localize.plugin_explorer_rename_subtitle(),
|
||||
Action = _ =>
|
||||
{
|
||||
RenameFile window;
|
||||
switch (record.Type)
|
||||
{
|
||||
case ResultType.Folder:
|
||||
window = new RenameFile(new DirectoryInfo(record.FullPath));
|
||||
break;
|
||||
case ResultType.File:
|
||||
window = new RenameFile(new FileInfo(record.FullPath));
|
||||
break;
|
||||
default:
|
||||
Context.API.ShowMsgError(Localize.plugin_explorer_cannot_rename());
|
||||
return false;
|
||||
}
|
||||
window.ShowDialog();
|
||||
|
||||
return false;
|
||||
|
||||
},
|
||||
// placeholder until real image is found
|
||||
IcoPath = Constants.RenameImagePath,
|
||||
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue8ac")
|
||||
|
||||
});
|
||||
|
||||
if (record.Type is ResultType.File or ResultType.Folder)
|
||||
contextMenus.Add(new Result
|
||||
|
|
|
|||
137
Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs
Normal file
137
Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Explorer.Helper;
|
||||
|
||||
public static class RenameThing
|
||||
{
|
||||
private static void Rename(this FileSystemInfo info, string newName)
|
||||
{
|
||||
if (info is FileInfo file)
|
||||
{
|
||||
if (!SharedCommands.FilesFolders.IsValidFileName(newName))
|
||||
{
|
||||
throw new InvalidNameException();
|
||||
}
|
||||
DirectoryInfo directory;
|
||||
var rootPath = Path.GetPathRoot(file.FullName);
|
||||
if (string.IsNullOrEmpty(rootPath)) return;
|
||||
directory = file.Directory ?? new DirectoryInfo(rootPath);
|
||||
string newPath = Path.Join(directory.FullName, newName);
|
||||
if (info.FullName == newPath)
|
||||
{
|
||||
throw new NotANewNameException("New name was the same as the old name");
|
||||
}
|
||||
if (File.Exists(newPath)) throw new ElementAlreadyExistsException();
|
||||
File.Move(info.FullName, newPath);
|
||||
return;
|
||||
}
|
||||
else if (info is DirectoryInfo directory)
|
||||
{
|
||||
if (!SharedCommands.FilesFolders.IsValidDirectoryName(newName))
|
||||
{
|
||||
throw new InvalidNameException();
|
||||
}
|
||||
DirectoryInfo parent;
|
||||
var rootPath = Path.GetPathRoot(directory.FullName);
|
||||
if (string.IsNullOrEmpty(rootPath)) return;
|
||||
parent = directory.Parent ?? new DirectoryInfo(rootPath);
|
||||
string newPath = Path.Join(parent.FullName, newName);
|
||||
if (info.FullName == newPath)
|
||||
{
|
||||
throw new NotANewNameException("New name was the same as the old name");
|
||||
}
|
||||
if (Directory.Exists(newPath)) throw new ElementAlreadyExistsException();
|
||||
|
||||
Directory.Move(info.FullName, newPath);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException($"{nameof(info)} must be either, {nameof(FileInfo)} or {nameof(DirectoryInfo)}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renames a file system element (directory or file)
|
||||
/// </summary>
|
||||
/// <param name="NewFileName">The requested new name</param>
|
||||
/// <param name="oldInfo"> The <see cref="FileInfo"/> or <see cref="DirectoryInfo"/> representing the old file</param>
|
||||
/// <param name="api">An instance of <see cref="IPublicAPI"/>so this can create msgboxes</param>
|
||||
public static void Rename(string NewFileName, FileSystemInfo oldInfo)
|
||||
{
|
||||
// if it's just whitespace and nothing else
|
||||
if (string.IsNullOrWhiteSpace(NewFileName))
|
||||
{
|
||||
Main.Context.API.ShowMsgError(Localize.plugin_explorer_field_may_not_be_empty());
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
oldInfo.Rename(NewFileName);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
switch (exception)
|
||||
{
|
||||
case FileNotFoundException:
|
||||
Main.Context.API.ShowMsgError(Localize.plugin_explorer_item_not_found(oldInfo.FullName));
|
||||
return;
|
||||
case NotANewNameException:
|
||||
Main.Context.API.ShowMsgError(Localize.plugin_explorer_not_a_new_name(NewFileName));
|
||||
return;
|
||||
case InvalidNameException:
|
||||
Main.Context.API.ShowMsgError(Localize.plugin_explorer_invalid_name(NewFileName));
|
||||
return;
|
||||
case ElementAlreadyExistsException:
|
||||
Main.Context.API.ShowMsgError(Localize.plugin_explorer_element_already_exists(NewFileName));
|
||||
return;
|
||||
default:
|
||||
string msg = exception.Message;
|
||||
if (!string.IsNullOrEmpty(msg))
|
||||
{
|
||||
Main.Context.API.ShowMsgError(Localize.plugin_explorer_exception(exception.Message));
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
Main.Context.API.ShowMsgError(Localize.plugin_explorer_no_reason_given_exception());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Main.Context.API.ShowMsg(Localize.plugin_explorer_successful_rename(NewFileName));
|
||||
}
|
||||
}
|
||||
|
||||
internal class NotANewNameException : IOException
|
||||
{
|
||||
public NotANewNameException() { }
|
||||
public NotANewNameException(string message) : base(message) { }
|
||||
public NotANewNameException(string message, Exception inner) : base(message, inner) { }
|
||||
protected NotANewNameException(
|
||||
SerializationInfo info,
|
||||
StreamingContext context) : base(info, context) { }
|
||||
}
|
||||
internal class ElementAlreadyExistsException : IOException {
|
||||
public ElementAlreadyExistsException() { }
|
||||
public ElementAlreadyExistsException(string message) : base(message) { }
|
||||
public ElementAlreadyExistsException(string message, Exception inner) : base(message, inner) { }
|
||||
protected ElementAlreadyExistsException(
|
||||
SerializationInfo info,
|
||||
StreamingContext context) : base(info, context) { }
|
||||
}
|
||||
|
||||
internal class InvalidNameException : Exception
|
||||
{
|
||||
public InvalidNameException() { }
|
||||
public InvalidNameException(string message) : base(message) { }
|
||||
public InvalidNameException(string message, Exception inner) : base(message, inner) { }
|
||||
protected InvalidNameException(
|
||||
SerializationInfo info,
|
||||
StreamingContext context) : base(info, context) { }
|
||||
}
|
||||
BIN
Plugins/Flow.Launcher.Plugin.Explorer/Images/rename.png
Normal file
BIN
Plugins/Flow.Launcher.Plugin.Explorer/Images/rename.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 514 B |
|
|
@ -140,6 +140,7 @@
|
|||
<system:String x:Key="plugin_explorer_fail_to_open">Fail to open {0}</system:String>
|
||||
<system:String x:Key="plugin_explorer_fail_to_set_text">Fail to set text in clipboard</system:String>
|
||||
<system:String x:Key="plugin_explorer_fail_to_set_files">Fail to set files/folders in clipboard</system:String>
|
||||
<system:String x:Key="plugin_explorer_run_as_administrator">Run As Administrator</system:String>
|
||||
|
||||
<!-- Special Results -->
|
||||
<system:String x:Key="plugin_explorer_diskfreespace">{0} free of {1}</system:String>
|
||||
|
|
@ -212,4 +213,20 @@
|
|||
<system:String x:Key="MonthsAgo">{0} months ago</system:String>
|
||||
<system:String x:Key="OneYearAgo">1 year ago</system:String>
|
||||
<system:String x:Key="YearsAgo">{0} years ago</system:String>
|
||||
|
||||
<!-- Rename File Dialog -->
|
||||
<system:String x:Key="plugin_explorer_new_file_name">New name</system:String>
|
||||
<system:String x:Key="plugin_explorer_rename_file_done">Rename</system:String>
|
||||
<system:String x:Key="plugin_explorer_rename_a_file">Rename</system:String>
|
||||
<system:String x:Key="plugin_explorer_not_a_new_name">The given name: {0} was not new.</system:String>
|
||||
<system:String x:Key="plugin_explorer_field_may_not_be_empty">New file name should not be empty.</system:String>
|
||||
<system:String x:Key="plugin_explorer_invalid_name">{0} is an invalid name.</system:String>
|
||||
<system:String x:Key="plugin_explorer_item_not_found">The specified item: {0} was not found</system:String>
|
||||
<system:String x:Key="plugin_explorer_rename_subtitle">Open a dialog to rename file or folder</system:String>
|
||||
<system:String x:Key="plugin_explorer_cannot_rename">This cannot be renamed.</system:String>
|
||||
<system:String x:Key="plugin_explorer_successful_rename">Successfully renamed it to: {0}</system:String>
|
||||
<system:String x:Key="plugin_explorer_element_already_exists">There is already a file with the name: {0} in this location</system:String>
|
||||
<system:String x:Key="plugin_explorer_failed_to_open_rename_dialog">Failed to open rename dialog.</system:String>
|
||||
<system:String x:Key="plugin_explorer_exception">An error occurred: {0}.</system:String>
|
||||
<system:String x:Key="plugin_explorer_no_reason_given_exception">An error occurred and no reason was given.</system:String>
|
||||
</ResourceDictionary>
|
||||
|
|
|
|||
|
|
@ -1,26 +1,28 @@
|
|||
using Flow.Launcher.Plugin.Explorer.Helper;
|
||||
using Flow.Launcher.Plugin.Explorer.Search;
|
||||
using Flow.Launcher.Plugin.Explorer.Search.Everything;
|
||||
using Flow.Launcher.Plugin.Explorer.ViewModels;
|
||||
using Flow.Launcher.Plugin.Explorer.Views;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
using Flow.Launcher.Plugin.Explorer.Exceptions;
|
||||
using System.Linq;
|
||||
using System.Globalization;
|
||||
using Flow.Launcher.Plugin.Explorer.Helper;
|
||||
using Flow.Launcher.Plugin.Explorer.Search;
|
||||
using Flow.Launcher.Plugin.Explorer.Search.Everything;
|
||||
using Flow.Launcher.Plugin.Explorer.ViewModels;
|
||||
using Flow.Launcher.Plugin.Explorer.Views;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Explorer
|
||||
{
|
||||
public class Main : ISettingProvider, IAsyncPlugin, IContextMenu, IPluginI18n, IAsyncDialogJump
|
||||
public class Main : ISettingProvider, IAsyncPlugin, IContextMenu, IPluginI18n, IAsyncDialogJump, IPluginHotkey
|
||||
{
|
||||
internal static PluginInitContext Context { get; set; }
|
||||
|
||||
internal static Settings Settings { get; set; }
|
||||
|
||||
private static readonly string ClassName = nameof(Main);
|
||||
|
||||
private SettingsViewModel viewModel;
|
||||
|
||||
private ContextMenu contextMenu;
|
||||
|
|
@ -48,6 +50,7 @@ namespace Flow.Launcher.Plugin.Explorer
|
|||
|
||||
EverythingApiDllImport.Load(Path.Combine(Context.CurrentPluginMetadata.PluginDirectory, "EverythingSDK",
|
||||
Environment.Is64BitProcess ? "x64" : "x86"));
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
|
@ -128,5 +131,148 @@ namespace Flow.Launcher.Plugin.Explorer
|
|||
return _emptyDialogJumpResultList;
|
||||
}
|
||||
}
|
||||
|
||||
public List<BasePluginHotkey> GetPluginHotkeys()
|
||||
{
|
||||
return new List<BasePluginHotkey>
|
||||
{
|
||||
new SearchWindowPluginHotkey()
|
||||
{
|
||||
Id = 0,
|
||||
Name = Localize.plugin_explorer_opencontainingfolder(),
|
||||
Description = Localize.plugin_explorer_opencontainingfolder_subtitle(),
|
||||
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue838"),
|
||||
DefaultHotkey = "Ctrl+Enter",
|
||||
Editable = false,
|
||||
Visible = true,
|
||||
Action = (r) =>
|
||||
{
|
||||
if (r.ContextData is SearchResult record)
|
||||
{
|
||||
if (record.Type is ResultType.File)
|
||||
{
|
||||
ResultManager.OpenFolder(record.FullPath, record.FullPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
Context.API.OpenDirectory(Path.GetDirectoryName(record.FullPath), record.FullPath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var message = $"Fail to open file at {record.FullPath}";
|
||||
Context.API.LogException(ClassName, message, e);
|
||||
Context.API.ShowMsgBox(e.Message, Localize.plugin_explorer_opendir_error());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
},
|
||||
new SearchWindowPluginHotkey()
|
||||
{
|
||||
Id = 1,
|
||||
Name = Localize.plugin_explorer_show_contextmenu_title(),
|
||||
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue700"),
|
||||
DefaultHotkey = "Alt+Enter",
|
||||
Editable = false,
|
||||
Visible = true,
|
||||
Action = (r) =>
|
||||
{
|
||||
if (r.ContextData is SearchResult record && record.Type is not ResultType.Volume)
|
||||
{
|
||||
try
|
||||
{
|
||||
ResultManager.ShowNativeContextMenu(record.FullPath, record.Type);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var message = $"Fail to show context menu for {record.FullPath}";
|
||||
Context.API.LogException(ClassName, message, e);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
},
|
||||
new SearchWindowPluginHotkey()
|
||||
{
|
||||
Id = 2,
|
||||
Name = Localize.plugin_explorer_run_as_administrator(),
|
||||
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\uE7EF"),
|
||||
DefaultHotkey = "Ctrl+Shift+Enter",
|
||||
Editable = false,
|
||||
Visible = true,
|
||||
Action = (r) =>
|
||||
{
|
||||
if (r.ContextData is SearchResult record)
|
||||
{
|
||||
if (record.Type is ResultType.File)
|
||||
{
|
||||
var filePath = record.FullPath;
|
||||
ResultManager.OpenFile(filePath, Settings.UseLocationAsWorkingDir ? Path.GetDirectoryName(filePath) : string.Empty, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
ResultManager.OpenFolder(record.FullPath);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var message = $"Fail to open file at {record.FullPath}";
|
||||
Context.API.LogException(ClassName, message, ex);
|
||||
Context.API.ShowMsgBox(ex.Message, Localize.plugin_explorer_opendir_error());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
},
|
||||
new SearchWindowPluginHotkey()
|
||||
{
|
||||
Id = 3,
|
||||
Name = Localize.plugin_explorer_rename_a_file(),
|
||||
Description = Localize.plugin_explorer_rename_subtitle(),
|
||||
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue8ac"),
|
||||
DefaultHotkey = "F2",
|
||||
Editable = true,
|
||||
Visible = true,
|
||||
Action = (r) =>
|
||||
{
|
||||
if (r.ContextData is SearchResult record)
|
||||
{
|
||||
RenameFile window;
|
||||
switch (record.Type)
|
||||
{
|
||||
case ResultType.Folder:
|
||||
window = new RenameFile(new DirectoryInfo(record.FullPath));
|
||||
break;
|
||||
case ResultType.File:
|
||||
window = new RenameFile(new FileInfo(record.FullPath));
|
||||
break;
|
||||
default:
|
||||
Context.API.ShowMsgError(Localize.plugin_explorer_cannot_rename());
|
||||
return false;
|
||||
}
|
||||
window.ShowDialog();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
internal const string QuickAccessImagePath = "Images\\quickaccess.png";
|
||||
internal const string RemoveQuickAccessImagePath = "Images\\removequickaccess.png";
|
||||
internal const string ShowContextMenuImagePath = "Images\\context_menu.png";
|
||||
internal const string RenameImagePath = "Images\\rename.png";
|
||||
internal const string EverythingErrorImagePath = "Images\\everything_error.png";
|
||||
internal const string IndexSearchWarningImagePath = "Images\\index_error.png";
|
||||
internal const string WindowsIndexErrorImagePath = "Images\\index_error2.png";
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using Flow.Launcher.Plugin.Explorer.Search.Everything;
|
||||
using Flow.Launcher.Plugin.Explorer.Views;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
|
|
@ -109,40 +109,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
PreviewPanel = new Lazy<UserControl>(() => new PreviewPanel(Settings, path, ResultType.Folder)),
|
||||
Action = c =>
|
||||
{
|
||||
if (c.SpecialKeyState.ToModifierKeys() == ModifierKeys.Alt)
|
||||
{
|
||||
ShowNativeContextMenu(path, ResultType.Folder);
|
||||
return false;
|
||||
}
|
||||
// open folder
|
||||
if (c.SpecialKeyState.ToModifierKeys() == (ModifierKeys.Control | ModifierKeys.Shift))
|
||||
{
|
||||
try
|
||||
{
|
||||
OpenFolder(path);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Context.API.ShowMsgBox(ex.Message, Localize.plugin_explorer_opendir_error());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Open containing folder
|
||||
if (c.SpecialKeyState.ToModifierKeys() == ModifierKeys.Control)
|
||||
{
|
||||
try
|
||||
{
|
||||
Context.API.OpenDirectory(Path.GetDirectoryName(path), path);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Context.API.ShowMsgBox(ex.Message, Localize.plugin_explorer_opendir_error());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If path search is disabled just open it in file manager
|
||||
if (Settings.DefaultOpenFolderInFileManager || (!Settings.PathSearchKeywordEnabled && !Settings.SearchActionKeywordEnabled))
|
||||
{
|
||||
|
|
@ -168,7 +134,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
Score = score,
|
||||
TitleToolTip = Localize.plugin_explorer_plugin_ToolTipOpenDirectory(),
|
||||
SubTitleToolTip = Settings.DisplayMoreInformationInToolTip ? GetFolderMoreInfoTooltip(path) : path,
|
||||
ContextData = new SearchResult { Type = ResultType.Folder, FullPath = path, WindowsIndexed = windowsIndexed }
|
||||
ContextData = new SearchResult { Type = ResultType.Folder, FullPath = path, WindowsIndexed = windowsIndexed },
|
||||
HotkeyIds = new List<int>
|
||||
{
|
||||
0, 1, 2, 3
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -270,15 +240,14 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
CopyText = folderPath,
|
||||
Action = c =>
|
||||
{
|
||||
if (c.SpecialKeyState.ToModifierKeys() == ModifierKeys.Alt)
|
||||
{
|
||||
ShowNativeContextMenu(folderPath, ResultType.Folder);
|
||||
return false;
|
||||
}
|
||||
OpenFolder(folderPath);
|
||||
return true;
|
||||
},
|
||||
ContextData = new SearchResult { Type = ResultType.Folder, FullPath = folderPath, WindowsIndexed = windowsIndexed }
|
||||
ContextData = new SearchResult { Type = ResultType.Folder, FullPath = folderPath, WindowsIndexed = windowsIndexed },
|
||||
HotkeyIds = new List<int>
|
||||
{
|
||||
1
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -308,25 +277,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
PreviewPanel = new Lazy<UserControl>(() => new PreviewPanel(Settings, filePath, ResultType.File)),
|
||||
Action = c =>
|
||||
{
|
||||
if (c.SpecialKeyState.ToModifierKeys() == ModifierKeys.Alt)
|
||||
{
|
||||
ShowNativeContextMenu(filePath, ResultType.File);
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
if (c.SpecialKeyState.ToModifierKeys() == (ModifierKeys.Control | ModifierKeys.Shift))
|
||||
{
|
||||
OpenFile(filePath, Settings.UseLocationAsWorkingDir ? directory : string.Empty, true);
|
||||
}
|
||||
else if (c.SpecialKeyState.ToModifierKeys() == ModifierKeys.Control)
|
||||
{
|
||||
OpenFolder(filePath, filePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
OpenFile(filePath, Settings.UseLocationAsWorkingDir ? directory : string.Empty);
|
||||
}
|
||||
OpenFile(filePath, Settings.UseLocationAsWorkingDir ? directory : string.Empty);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -337,7 +290,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
},
|
||||
TitleToolTip = Localize.plugin_explorer_plugin_ToolTipOpenContainingFolder(),
|
||||
SubTitleToolTip = Settings.DisplayMoreInformationInToolTip ? GetFileMoreInfoTooltip(filePath) : filePath,
|
||||
ContextData = new SearchResult { Type = ResultType.File, FullPath = filePath, WindowsIndexed = windowsIndexed }
|
||||
ContextData = new SearchResult { Type = ResultType.File, FullPath = filePath, WindowsIndexed = windowsIndexed },
|
||||
HotkeyIds = new List<int>
|
||||
{
|
||||
0, 1, 2, 3
|
||||
},
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
|
@ -349,13 +306,13 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
return MediaExtensions.Contains(extension.ToLowerInvariant());
|
||||
}
|
||||
|
||||
private static void OpenFile(string filePath, string workingDir = "", bool asAdmin = false)
|
||||
public static void OpenFile(string filePath, string workingDir = "", bool asAdmin = false)
|
||||
{
|
||||
IncrementEverythingRunCounterIfNeeded(filePath);
|
||||
FilesFolders.OpenFile(filePath, workingDir, asAdmin, (string str) => Context.API.ShowMsgBox(str));
|
||||
}
|
||||
|
||||
private static void OpenFolder(string folderPath, string fileNameOrFilePath = null)
|
||||
public static void OpenFolder(string folderPath, string fileNameOrFilePath = null)
|
||||
{
|
||||
IncrementEverythingRunCounterIfNeeded(folderPath);
|
||||
Context.API.OpenDirectory(folderPath, fileNameOrFilePath);
|
||||
|
|
|
|||
114
Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml
Normal file
114
Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
<Window
|
||||
x:Class="Flow.Launcher.Plugin.Explorer.Views.RenameFile"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Flow.Launcher.Plugin.Explorer.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Title=""
|
||||
Height="180"
|
||||
MaxWidth="600"
|
||||
Background="{DynamicResource PopuBGColor}"
|
||||
DataContext="{Binding RelativeSource={RelativeSource Self}}"
|
||||
Foreground="{DynamicResource PopupTextColor}"
|
||||
ResizeMode="NoResize"
|
||||
SizeToContent="Width"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
mc:Ignorable="d">
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="32" ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
|
||||
</WindowChrome.WindowChrome>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="60" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Grid.Row="0">
|
||||
<StackPanel>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button
|
||||
Grid.Column="4"
|
||||
Click="BtnCancel"
|
||||
Style="{StaticResource TitleBarCloseButtonStyle}">
|
||||
<Path
|
||||
Width="46"
|
||||
Height="32"
|
||||
Data="M 18,11 27,20 M 18,20 27,11"
|
||||
Stroke="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
|
||||
StrokeThickness="1">
|
||||
<Path.Style>
|
||||
<Style TargetType="Path">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Path=IsActive, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Value="False">
|
||||
<Setter Property="Opacity" Value="0.5" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Path.Style>
|
||||
</Path>
|
||||
</Button>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="26 0 26 0">
|
||||
<StackPanel Margin="0 0 0 0">
|
||||
<TextBlock
|
||||
Margin="0 0 0 0"
|
||||
FontSize="20"
|
||||
FontWeight="SemiBold"
|
||||
Text="{DynamicResource plugin_explorer_rename_a_file}"
|
||||
TextAlignment="Left" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="0 10 0 0" Orientation="Horizontal">
|
||||
<TextBlock
|
||||
MinWidth="150"
|
||||
Margin="0 10 5 10"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="{DynamicResource plugin_explorer_new_file_name}" />
|
||||
<TextBox
|
||||
Name="RenameTb"
|
||||
Width="300"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
GotFocus="SelectAll_OnTextBoxGotFocus"
|
||||
PreviewKeyDown="RenameTb_OnKeyDown"
|
||||
Text="{Binding NewFileName}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<Border
|
||||
Grid.Row="1"
|
||||
Background="{DynamicResource PopupButtonAreaBGColor}"
|
||||
BorderBrush="{DynamicResource PopupButtonAreaBorderColor}"
|
||||
BorderThickness="0 1 0 0">
|
||||
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
|
||||
<Button
|
||||
x:Name="btnCancel"
|
||||
Width="145"
|
||||
Height="34"
|
||||
Margin="5 0 5 0"
|
||||
Click="BtnCancel"
|
||||
Content="{DynamicResource cancel}" />
|
||||
<Button
|
||||
Name="btnDone"
|
||||
Width="145"
|
||||
Height="34"
|
||||
Margin="5 0 5 0"
|
||||
Click="OnDoneButtonClick"
|
||||
Style="{StaticResource AccentButtonStyle}">
|
||||
<TextBlock x:Name="lblAdd" Text="{DynamicResource plugin_explorer_rename_file_done}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Flow.Launcher.Plugin.Explorer.Helper;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Explorer.Views
|
||||
{
|
||||
[INotifyPropertyChanged]
|
||||
public partial class RenameFile : Window
|
||||
{
|
||||
public string NewFileName
|
||||
{
|
||||
get => _newFileName;
|
||||
set
|
||||
{
|
||||
_ = SetProperty(ref _newFileName, value);
|
||||
}
|
||||
}
|
||||
|
||||
private string _newFileName;
|
||||
|
||||
private readonly string _oldFilePath;
|
||||
|
||||
private readonly FileSystemInfo _info;
|
||||
|
||||
public RenameFile(FileSystemInfo info)
|
||||
{
|
||||
_info = info;
|
||||
_oldFilePath = _info.FullName;
|
||||
NewFileName = _info.Name;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
ShowInTaskbar = false;
|
||||
RenameTb.Focus();
|
||||
KeyDown += (s, e) =>
|
||||
{
|
||||
if (e.Key == Key.Escape)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// https://stackoverflow.com/a/59560352/24045055
|
||||
/// </summary>
|
||||
private async void SelectAll_OnTextBoxGotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is not TextBox textBox) return;
|
||||
if (_info is DirectoryInfo)
|
||||
{
|
||||
await textBox.Dispatcher.InvokeAsync(textBox.SelectAll, DispatcherPriority.Background);
|
||||
return;
|
||||
}
|
||||
// select everything but the extension
|
||||
if (_info is FileInfo info)
|
||||
{
|
||||
string properName = Path.GetFileNameWithoutExtension(info.Name);
|
||||
int start = textBox.Text.LastIndexOf(properName, StringComparison.OrdinalIgnoreCase);
|
||||
if (start < 0)
|
||||
{
|
||||
await textBox.Dispatcher.InvokeAsync(textBox.SelectAll, DispatcherPriority.Background);
|
||||
return;
|
||||
}
|
||||
await textBox.Dispatcher.InvokeAsync(() => textBox.Select(start, properName.Length), DispatcherPriority.Background);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnDoneButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
RenameThing.Rename(NewFileName, _info);
|
||||
// Close the dialog no matter if it worked or not because error messages are popped up in RenameThing
|
||||
Close();
|
||||
}
|
||||
|
||||
private void BtnCancel(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
private void RenameTb_OnKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Enter)
|
||||
{
|
||||
btnDone.Focus();
|
||||
OnDoneButtonClick(sender, e);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Controls;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
using Flow.Launcher.Plugin.PluginsManager.ViewModels;
|
||||
using Flow.Launcher.Plugin.PluginsManager.Views;
|
||||
|
||||
namespace Flow.Launcher.Plugin.PluginsManager
|
||||
{
|
||||
public class Main : ISettingProvider, IAsyncPlugin, IContextMenu, IPluginI18n
|
||||
public class Main : ISettingProvider, IAsyncPlugin, IContextMenu, IPluginI18n, IPluginHotkey
|
||||
{
|
||||
internal static PluginInitContext Context { get; set; }
|
||||
|
||||
|
|
@ -69,5 +69,36 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
{
|
||||
return Context.API.GetTranslation("plugin_pluginsmanager_plugin_description");
|
||||
}
|
||||
|
||||
public List<BasePluginHotkey> GetPluginHotkeys()
|
||||
{
|
||||
return new List<BasePluginHotkey>
|
||||
{
|
||||
new SearchWindowPluginHotkey
|
||||
{
|
||||
Id = 0,
|
||||
Name = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_openwebsite_title"),
|
||||
Description = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_openwebsite_subtitle"),
|
||||
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\uEB41"),
|
||||
DefaultHotkey = "Ctrl+Enter",
|
||||
Editable = false,
|
||||
Visible = true,
|
||||
Action = (r) =>
|
||||
{
|
||||
if (r.ContextData is UserPlugin plugin)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(plugin.Website))
|
||||
{
|
||||
Context.API.OpenUrl(plugin.Website);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -421,12 +421,15 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
|
||||
return true;
|
||||
},
|
||||
ContextData =
|
||||
new UserPlugin
|
||||
{
|
||||
Website = x.PluginNewUserPlugin.Website,
|
||||
UrlSourceCode = x.PluginNewUserPlugin.UrlSourceCode
|
||||
}
|
||||
ContextData = new UserPlugin
|
||||
{
|
||||
Website = x.PluginNewUserPlugin.Website,
|
||||
UrlSourceCode = x.PluginNewUserPlugin.UrlSourceCode
|
||||
},
|
||||
HotkeyIds = new List<int>
|
||||
{
|
||||
0
|
||||
},
|
||||
});
|
||||
|
||||
// Update all result
|
||||
|
|
@ -574,12 +577,6 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
IcoPath = icoPath,
|
||||
Action = e =>
|
||||
{
|
||||
if (e.SpecialKeyState.CtrlPressed)
|
||||
{
|
||||
SearchWeb.OpenInBrowserTab(plugin.UrlDownload);
|
||||
return ShouldHideWindow;
|
||||
}
|
||||
|
||||
if (Settings.WarnFromUnknownSource)
|
||||
{
|
||||
if (!InstallSourceKnown(plugin.UrlDownload)
|
||||
|
|
@ -697,17 +694,15 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
IcoPath = x.IcoPath,
|
||||
Action = e =>
|
||||
{
|
||||
if (e.SpecialKeyState.CtrlPressed)
|
||||
{
|
||||
SearchWeb.OpenInBrowserTab(x.Website);
|
||||
return ShouldHideWindow;
|
||||
}
|
||||
|
||||
Context.API.HideMainWindow();
|
||||
_ = InstallOrUpdateAsync(x); // No need to wait
|
||||
return ShouldHideWindow;
|
||||
},
|
||||
ContextData = x
|
||||
ContextData = x,
|
||||
HotkeyIds = new List<int>
|
||||
{
|
||||
0
|
||||
},
|
||||
});
|
||||
|
||||
return Search(results, search);
|
||||
|
|
@ -816,7 +811,15 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
},
|
||||
ContextData = new UserPlugin
|
||||
{
|
||||
Website = x.Metadata.Website
|
||||
},
|
||||
HotkeyIds = new List<int>
|
||||
{
|
||||
0
|
||||
},
|
||||
});
|
||||
|
||||
return Search(results, search);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ using Path = System.IO.Path;
|
|||
|
||||
namespace Flow.Launcher.Plugin.Program
|
||||
{
|
||||
public class Main : ISettingProvider, IAsyncPlugin, IPluginI18n, IContextMenu, IAsyncReloadable, IDisposable
|
||||
public class Main : ISettingProvider, IAsyncPlugin, IPluginI18n, IContextMenu, IAsyncReloadable, IDisposable, IPluginHotkey
|
||||
{
|
||||
private static readonly string ClassName = nameof(Main);
|
||||
|
||||
|
|
@ -560,5 +560,59 @@ namespace Flow.Launcher.Plugin.Program
|
|||
{
|
||||
Win32.Dispose();
|
||||
}
|
||||
|
||||
public List<BasePluginHotkey> GetPluginHotkeys()
|
||||
{
|
||||
return new List<BasePluginHotkey>
|
||||
{
|
||||
new SearchWindowPluginHotkey()
|
||||
{
|
||||
Id = 0,
|
||||
Name = Context.API.GetTranslation("flowlauncher_plugin_program_open_containing_folder"),
|
||||
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue838"),
|
||||
DefaultHotkey = "Ctrl+Enter",
|
||||
Editable = false,
|
||||
Visible = true,
|
||||
Action = (r) =>
|
||||
{
|
||||
if (r?.ContextData is UWPApp uwp)
|
||||
{
|
||||
Context.API.OpenDirectory(uwp.Location);
|
||||
return true;
|
||||
}
|
||||
else if (r?.ContextData is Win32 win32)
|
||||
{
|
||||
Context.API.OpenDirectory(win32.ParentDirectory, win32.FullPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
},
|
||||
// TODO: Do it after administrator mode PR
|
||||
/*new SearchWindowPluginHotkey()
|
||||
{
|
||||
Id = 1,
|
||||
Name = Context.API.GetTranslation("flowlauncher_plugin_program_run_as_administrator"),
|
||||
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\uE7EF"),
|
||||
DefaultHotkey = "Ctrl+Shift+Enter",
|
||||
Editable = false,
|
||||
Visible = true,
|
||||
Action = (r) =>
|
||||
{
|
||||
if (r.ContextData is UWPApp uwp)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (r.ContextData is Win32 win32)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
},*/
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -443,14 +443,6 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
ContextData = this,
|
||||
Action = e =>
|
||||
{
|
||||
// Ctrl + Enter to open containing folder
|
||||
bool openFolder = e.SpecialKeyState.ToModifierKeys() == ModifierKeys.Control;
|
||||
if (openFolder)
|
||||
{
|
||||
Main.Context.API.OpenDirectory(Location);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ctrl + Shift + Enter to run elevated
|
||||
bool elevated = e.SpecialKeyState.ToModifierKeys() == (ModifierKeys.Control | ModifierKeys.Shift);
|
||||
|
||||
|
|
@ -466,7 +458,11 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
},
|
||||
HotkeyIds = new List<int>
|
||||
{
|
||||
0
|
||||
},
|
||||
};
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -185,14 +185,6 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
TitleToolTip = $"{title}\n{ExecutablePath}",
|
||||
Action = c =>
|
||||
{
|
||||
// Ctrl + Enter to open containing folder
|
||||
bool openFolder = c.SpecialKeyState.ToModifierKeys() == ModifierKeys.Control;
|
||||
if (openFolder)
|
||||
{
|
||||
Main.Context.API.OpenDirectory(ParentDirectory, FullPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ctrl + Shift + Enter to run as admin
|
||||
bool runAsAdmin = c.SpecialKeyState.ToModifierKeys() == (ModifierKeys.Control | ModifierKeys.Shift);
|
||||
|
||||
|
|
@ -207,7 +199,11 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
_ = Task.Run(() => Main.StartProcess(Process.Start, info));
|
||||
|
||||
return true;
|
||||
}
|
||||
},
|
||||
HotkeyIds = new List<int>
|
||||
{
|
||||
0
|
||||
},
|
||||
};
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -286,16 +286,12 @@ Or download the [early access version](https://github.com/Flow-Launcher/Prerelea
|
|||
| ------------------------------------------------------------------------- | ----------------------------------------------- |
|
||||
| <kbd>Alt</kbd>+<kbd>Space</kbd> | Open search window (default and configurable) |
|
||||
| <kbd>Enter</kbd> | Execute |
|
||||
| <kbd>Ctrl</kbd>+<kbd>Enter</kbd> | Open containing folder |
|
||||
| <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Enter</kbd> | Run as admin |
|
||||
| <kbd>↑</kbd>/<kbd>↓</kbd>, <kbd>Shift</kbd>+<kbd>Tab</kbd>/<kbd>Tab</kbd> | Previous / Next result |
|
||||
| <kbd>←</kbd>/<kbd>→</kbd> | Back to result / Open Context Menu |
|
||||
| <kbd>Ctrl</kbd>+<kbd>O</kbd> , <kbd>Shift</kbd>+<kbd>Enter</kbd> | Open Context Menu |
|
||||
| <kbd>Ctrl</kbd>+<kbd>Tab</kbd> | Autocomplete |
|
||||
| <kbd>F1</kbd> | Toggle Preview Panel (default and configurable) |
|
||||
| <kbd>Esc</kbd> | Back to results / hide search window |
|
||||
| <kbd>Ctrl</kbd>+<kbd>C</kbd> | Copy folder / file |
|
||||
| <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>C</kbd> | Copy folder / file path |
|
||||
| <kbd>Ctrl</kbd>+<kbd>I</kbd> | Open Flow's settings |
|
||||
| <kbd>Ctrl</kbd>+<kbd>R</kbd> | Run the current query again (refresh results) |
|
||||
| <kbd>F5</kbd> | Reload all plugin data |
|
||||
|
|
|
|||
Loading…
Reference in a new issue