diff --git a/.editorconfig b/.editorconfig
index 11a0bcdf6..e1b8ed79e 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -58,7 +58,7 @@ dotnet_style_prefer_conditional_expression_over_return = true:silent
###############################
# Style Definitions
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
-# Use PascalCase for constant fields
+# Use PascalCase for constant fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
@@ -134,7 +134,7 @@ csharp_preserve_single_line_statements = true
csharp_preserve_single_line_blocks = true
csharp_using_directive_placement = outside_namespace:silent
csharp_prefer_simple_using_statement = true:suggestion
-csharp_style_namespace_declarations = block_scoped:silent
+csharp_style_namespace_declarations = file_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt
index 19f257ead..7845c6a65 100644
--- a/.github/actions/spelling/allow.txt
+++ b/.github/actions/spelling/allow.txt
@@ -21,3 +21,6 @@ vkcode
winget
workaround
nupkg
+Firefox
+Português
+Português (Brasil)
diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt
index ca2edec26..f40a9dd36 100644
--- a/.github/actions/spelling/expect.txt
+++ b/.github/actions/spelling/expect.txt
@@ -74,6 +74,7 @@ WCA_ACCENT_POLICY
HGlobal
dopusrt
firefox
+Firefox
msedge
svgc
ime
@@ -97,6 +98,7 @@ Português
Português (Brasil)
Italiano
Slovenský
+Tiếng Việt
Droplex
Preinstalled
errormetadatafile
diff --git a/.github/workflows/default_plugins.yml b/.github/workflows/default_plugins.yml
index 2cb04fe0d..a0a816877 100644
--- a/.github/workflows/default_plugins.yml
+++ b/.github/workflows/default_plugins.yml
@@ -65,7 +65,7 @@ jobs:
- name: Publish BrowserBookmark
if: steps.changes.outputs.browserbookmark == 'true'
- uses: softprops/action-gh-release@v1
+ uses: softprops/action-gh-release@v2
with:
repository: "Flow-Launcher/Flow.Launcher.Plugin.BrowserBookmark"
files: "Flow.Launcher.Plugin.BrowserBookmark.zip"
@@ -92,7 +92,7 @@ jobs:
- name: Publish Calculator
if: steps.changes.outputs.calculator == 'true'
- uses: softprops/action-gh-release@v1
+ uses: softprops/action-gh-release@v2
with:
repository: "Flow-Launcher/Flow.Launcher.Plugin.Calculator"
files: "Flow.Launcher.Plugin.Calculator.zip"
@@ -119,7 +119,7 @@ jobs:
- name: Publish Explorer
if: steps.changes.outputs.explorer == 'true'
- uses: softprops/action-gh-release@v1
+ uses: softprops/action-gh-release@v2
with:
repository: "Flow-Launcher/Flow.Launcher.Plugin.Explorer"
files: "Flow.Launcher.Plugin.Explorer.zip"
@@ -146,7 +146,7 @@ jobs:
- name: Publish PluginIndicator
if: steps.changes.outputs.pluginindicator == 'true'
- uses: softprops/action-gh-release@v1
+ uses: softprops/action-gh-release@v2
with:
repository: "Flow-Launcher/Flow.Launcher.Plugin.PluginIndicator"
files: "Flow.Launcher.Plugin.PluginIndicator.zip"
@@ -173,7 +173,7 @@ jobs:
- name: Publish PluginsManager
if: steps.changes.outputs.pluginsmanager == 'true'
- uses: softprops/action-gh-release@v1
+ uses: softprops/action-gh-release@v2
with:
repository: "Flow-Launcher/Flow.Launcher.Plugin.PluginsManager"
files: "Flow.Launcher.Plugin.PluginsManager.zip"
@@ -200,7 +200,7 @@ jobs:
- name: Publish ProcessKiller
if: steps.changes.outputs.processkiller == 'true'
- uses: softprops/action-gh-release@v1
+ uses: softprops/action-gh-release@v2
with:
repository: "Flow-Launcher/Flow.Launcher.Plugin.ProcessKiller"
files: "Flow.Launcher.Plugin.ProcessKiller.zip"
@@ -227,7 +227,7 @@ jobs:
- name: Publish Program
if: steps.changes.outputs.program == 'true'
- uses: softprops/action-gh-release@v1
+ uses: softprops/action-gh-release@v2
with:
repository: "Flow-Launcher/Flow.Launcher.Plugin.Program"
files: "Flow.Launcher.Plugin.Program.zip"
@@ -254,7 +254,7 @@ jobs:
- name: Publish Shell
if: steps.changes.outputs.shell == 'true'
- uses: softprops/action-gh-release@v1
+ uses: softprops/action-gh-release@v2
with:
repository: "Flow-Launcher/Flow.Launcher.Plugin.Shell"
files: "Flow.Launcher.Plugin.Shell.zip"
@@ -281,7 +281,7 @@ jobs:
- name: Publish Sys
if: steps.changes.outputs.sys == 'true'
- uses: softprops/action-gh-release@v1
+ uses: softprops/action-gh-release@v2
with:
repository: "Flow-Launcher/Flow.Launcher.Plugin.Sys"
files: "Flow.Launcher.Plugin.Sys.zip"
@@ -308,7 +308,7 @@ jobs:
- name: Publish Url
if: steps.changes.outputs.url == 'true'
- uses: softprops/action-gh-release@v1
+ uses: softprops/action-gh-release@v2
with:
repository: "Flow-Launcher/Flow.Launcher.Plugin.Url"
files: "Flow.Launcher.Plugin.Url.zip"
@@ -335,7 +335,7 @@ jobs:
- name: Publish WebSearch
if: steps.changes.outputs.websearch == 'true'
- uses: softprops/action-gh-release@v1
+ uses: softprops/action-gh-release@v2
with:
repository: "Flow-Launcher/Flow.Launcher.Plugin.WebSearch"
files: "Flow.Launcher.Plugin.WebSearch.zip"
@@ -362,7 +362,7 @@ jobs:
- name: Publish WindowsSettings
if: steps.changes.outputs.windowssettings == 'true'
- uses: softprops/action-gh-release@v1
+ uses: softprops/action-gh-release@v2
with:
repository: "Flow-Launcher/Flow.Launcher.Plugin.WindowsSettings"
files: "Flow.Launcher.Plugin.WindowsSettings.zip"
diff --git a/Flow.Launcher.Core/Resource/AvailableLanguages.cs b/Flow.Launcher.Core/Resource/AvailableLanguages.cs
index b6d394d11..46c1ecb54 100644
--- a/Flow.Launcher.Core/Resource/AvailableLanguages.cs
+++ b/Flow.Launcher.Core/Resource/AvailableLanguages.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
namespace Flow.Launcher.Core.Resource
{
@@ -27,6 +27,8 @@ namespace Flow.Launcher.Core.Resource
public static Language Turkish = new Language("tr", "Türkçe");
public static Language Czech = new Language("cs", "čeština");
public static Language Arabic = new Language("ar", "اللغة العربية");
+ public static Language Vietnamese = new Language("vi-vn", "Tiếng Việt");
+
public static List GetAvailableLanguages()
{
@@ -54,7 +56,8 @@ namespace Flow.Launcher.Core.Resource
Slovak,
Turkish,
Czech,
- Arabic
+ Arabic,
+ Vietnamese
};
return languages;
}
diff --git a/Flow.Launcher.Infrastructure/Constant.cs b/Flow.Launcher.Infrastructure/Constant.cs
index 7a95b52d5..8a95ee79f 100644
--- a/Flow.Launcher.Infrastructure/Constant.cs
+++ b/Flow.Launcher.Infrastructure/Constant.cs
@@ -7,6 +7,7 @@ namespace Flow.Launcher.Infrastructure
public static class Constant
{
public const string FlowLauncher = "Flow.Launcher";
+ public const string FlowLauncherFullName = "Flow Launcher";
public const string Plugins = "Plugins";
public const string PluginMetadataFileName = "plugin.json";
diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj
index 574f3686a..831091732 100644
--- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj
+++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj
@@ -54,7 +54,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs b/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs
index 937059436..25bc75a56 100644
--- a/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs
+++ b/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs
@@ -6,7 +6,7 @@ using System.Windows.Input;
namespace Flow.Launcher.Infrastructure.Hotkey
{
- public class HotkeyModel
+ public record struct HotkeyModel
{
public bool Alt { get; set; }
public bool Shift { get; set; }
@@ -17,8 +17,7 @@ namespace Flow.Launcher.Infrastructure.Hotkey
private static readonly Dictionary specialSymbolDictionary = new Dictionary
{
- {Key.Space, "Space"},
- {Key.Oem3, "~"}
+ { Key.Space, "Space" }, { Key.Oem3, "~" }
};
public ModifierKeys ModifierKeys
@@ -30,18 +29,22 @@ namespace Flow.Launcher.Infrastructure.Hotkey
{
modifierKeys |= ModifierKeys.Alt;
}
+
if (Shift)
{
modifierKeys |= ModifierKeys.Shift;
}
+
if (Win)
{
modifierKeys |= ModifierKeys.Windows;
}
+
if (Ctrl)
{
modifierKeys |= ModifierKeys.Control;
}
+
return modifierKeys;
}
}
@@ -66,31 +69,37 @@ namespace Flow.Launcher.Infrastructure.Hotkey
{
return;
}
+
List keys = hotkeyString.Replace(" ", "").Split('+').ToList();
if (keys.Contains("Alt"))
{
Alt = true;
keys.Remove("Alt");
}
+
if (keys.Contains("Shift"))
{
Shift = true;
keys.Remove("Shift");
}
+
if (keys.Contains("Win"))
{
Win = true;
keys.Remove("Win");
}
+
if (keys.Contains("Ctrl"))
{
Ctrl = true;
keys.Remove("Ctrl");
}
+
if (keys.Count == 1)
{
string charKey = keys[0];
- KeyValuePair? specialSymbolPair = specialSymbolDictionary.FirstOrDefault(pair => pair.Value == charKey);
+ KeyValuePair? specialSymbolPair =
+ specialSymbolDictionary.FirstOrDefault(pair => pair.Value == charKey);
if (specialSymbolPair.Value.Value != null)
{
CharKey = specialSymbolPair.Value.Key;
@@ -103,7 +112,6 @@ namespace Flow.Launcher.Infrastructure.Hotkey
}
catch (ArgumentException)
{
-
}
}
}
@@ -111,33 +119,39 @@ namespace Flow.Launcher.Infrastructure.Hotkey
public override string ToString()
{
- List keys = new List();
- if (Ctrl)
+ return string.Join(" + ", EnumerateDisplayKeys());
+ }
+
+ public IEnumerable EnumerateDisplayKeys()
+ {
+ if (Ctrl && CharKey is not (Key.LeftCtrl or Key.RightCtrl))
{
- keys.Add("Ctrl");
+ yield return "Ctrl";
}
- if (Alt)
+
+ if (Alt && CharKey is not (Key.LeftAlt or Key.RightAlt))
{
- keys.Add("Alt");
+ yield return "Alt";
}
- if (Shift)
+
+ if (Shift && CharKey is not (Key.LeftShift or Key.RightShift))
{
- keys.Add("Shift");
+ yield return "Shift";
}
- if (Win)
+
+ if (Win && CharKey is not (Key.LWin or Key.RWin))
{
- keys.Add("Win");
+ yield return "Win";
}
if (CharKey != Key.None)
{
- keys.Add(specialSymbolDictionary.ContainsKey(CharKey)
- ? specialSymbolDictionary[CharKey]
- : CharKey.ToString());
+ yield return specialSymbolDictionary.TryGetValue(CharKey, out var value)
+ ? value
+ : CharKey.ToString();
}
- return string.Join(" + ", keys);
}
-
+
///
/// Validate hotkey
///
@@ -164,11 +178,13 @@ namespace Flow.Launcher.Infrastructure.Hotkey
{
KeyGesture keyGesture = new KeyGesture(CharKey, ModifierKeys);
}
- catch (System.Exception e) when (e is NotSupportedException || e is InvalidEnumArgumentException)
+ catch (System.Exception e) when
+ (e is NotSupportedException || e is InvalidEnumArgumentException)
{
return false;
}
}
+
if (ModifierKeys == ModifierKeys.None)
{
return !IsPrintableCharacter(CharKey);
@@ -206,18 +222,6 @@ namespace Flow.Launcher.Infrastructure.Hotkey
key == Key.Decimal;
}
- public override bool Equals(object obj)
- {
- if (obj is HotkeyModel other)
- {
- return ModifierKeys == other.ModifierKeys && CharKey == other.CharKey;
- }
- else
- {
- return false;
- }
- }
-
public override int GetHashCode()
{
return HashCode.Combine(ModifierKeys, CharKey);
diff --git a/Flow.Launcher.Infrastructure/Hotkey/IHotkeySettings.cs b/Flow.Launcher.Infrastructure/Hotkey/IHotkeySettings.cs
new file mode 100644
index 000000000..448a70d19
--- /dev/null
+++ b/Flow.Launcher.Infrastructure/Hotkey/IHotkeySettings.cs
@@ -0,0 +1,17 @@
+using System.Collections.Generic;
+
+namespace Flow.Launcher.Infrastructure.Hotkey;
+
+///
+/// Interface that you should implement in your settings class to be able to pass it to
+/// Flow.Launcher.HotkeyControlDialog . It allows the dialog to display the hotkeys that have already been
+/// registered, and optionally provide a way to unregister them.
+///
+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.
+ ///
+ public List RegisteredHotkeys { get; }
+}
diff --git a/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs b/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs
new file mode 100644
index 000000000..b6a10fc30
--- /dev/null
+++ b/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs
@@ -0,0 +1,119 @@
+using System;
+
+namespace Flow.Launcher.Infrastructure.Hotkey;
+
+#nullable enable
+
+///
+/// Represents a hotkey that has been registered. Used in Flow.Launcher.HotkeyControlDialog via
+/// and to display errors if user tries to register a hotkey
+/// that has already been registered, and optionally provides a way to unregister the hotkey.
+///
+public record RegisteredHotkeyData
+{
+ ///
+ /// representation of this hotkey.
+ ///
+ public HotkeyModel Hotkey { get; }
+
+ ///
+ /// String key in the localization dictionary that represents this hotkey. For example, ReloadPluginHotkey ,
+ /// which represents the string "Reload Plugins Data" in en.xaml
+ ///
+ public string DescriptionResourceKey { get; }
+
+ ///
+ /// Array of values that will replace {0} , {1} , {2} , etc. in the localized string found via
+ /// .
+ ///
+ public object?[] DescriptionFormatVariables { get; } = Array.Empty();
+
+ ///
+ /// An action that, when called, will unregister this hotkey. If it's null , it's assumed that
+ /// this hotkey can't be unregistered, and the "Overwrite" option will not appear in the hotkey dialog.
+ ///
+ public Action? RemoveHotkey { get; }
+
+ ///
+ /// Creates an instance of RegisteredHotkeyData . Assumes that the key specified in
+ /// descriptionResourceKey doesn't need any arguments for string.Format . If it does,
+ /// use one of the other constructors.
+ ///
+ ///
+ /// The hotkey this class will represent.
+ /// Example values: F1 , Ctrl+Shift+Enter
+ ///
+ ///
+ /// The key in the localization dictionary that represents this hotkey. For example, ReloadPluginHotkey ,
+ /// which represents the string "Reload Plugins Data" in en.xaml
+ ///
+ ///
+ /// An action that, when called, will unregister this hotkey. If it's null , it's assumed that this hotkey
+ /// can't be unregistered, and the "Overwrite" option will not appear in the hotkey dialog.
+ ///
+ public RegisteredHotkeyData(string hotkey, string descriptionResourceKey, Action? removeHotkey = null)
+ {
+ Hotkey = new HotkeyModel(hotkey);
+ DescriptionResourceKey = descriptionResourceKey;
+ RemoveHotkey = removeHotkey;
+ }
+
+ ///
+ /// Creates an instance of RegisteredHotkeyData . Assumes that the key specified in
+ /// descriptionResourceKey needs exactly one argument for string.Format .
+ ///
+ ///
+ /// The hotkey this class will represent.
+ /// Example values: F1 , Ctrl+Shift+Enter
+ ///
+ ///
+ /// The key in the localization dictionary that represents this hotkey. For example, ReloadPluginHotkey ,
+ /// which represents the string "Reload Plugins Data" in en.xaml
+ ///
+ ///
+ /// The value that will replace {0} in the localized string found via description .
+ ///
+ ///
+ /// An action that, when called, will unregister this hotkey. If it's null , it's assumed that this hotkey
+ /// can't be unregistered, and the "Overwrite" option will not appear in the hotkey dialog.
+ ///
+ public RegisteredHotkeyData(
+ string hotkey, string descriptionResourceKey, object? descriptionFormatVariable, Action? removeHotkey = null
+ )
+ {
+ Hotkey = new HotkeyModel(hotkey);
+ DescriptionResourceKey = descriptionResourceKey;
+ DescriptionFormatVariables = new[] { descriptionFormatVariable };
+ RemoveHotkey = removeHotkey;
+ }
+
+ ///
+ /// Creates an instance of RegisteredHotkeyData . Assumes that the key specified in
+ /// needs multiple arguments for string.Format .
+ ///
+ ///
+ /// The hotkey this class will represent.
+ /// Example values: F1 , Ctrl+Shift+Enter
+ ///
+ ///
+ /// The key in the localization dictionary that represents this hotkey. For example, ReloadPluginHotkey ,
+ /// which represents the string "Reload Plugins Data" in en.xaml
+ ///
+ ///
+ /// Array of values that will replace {0} , {1} , {2} , etc.
+ /// in the localized string found via description .
+ ///
+ ///
+ /// An action that, when called, will unregister this hotkey. If it's null , it's assumed that this hotkey
+ /// can't be unregistered, and the "Overwrite" option will not appear in the hotkey dialog.
+ ///
+ public RegisteredHotkeyData(
+ string hotkey, string descriptionResourceKey, object?[] descriptionFormatVariables, Action? removeHotkey = null
+ )
+ {
+ Hotkey = new HotkeyModel(hotkey);
+ DescriptionResourceKey = descriptionResourceKey;
+ DescriptionFormatVariables = descriptionFormatVariables;
+ RemoveHotkey = removeHotkey;
+ }
+}
diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs
index ca1674315..7bb8fe200 100644
--- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs
+++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs
@@ -4,13 +4,14 @@ using System.Collections.ObjectModel;
using System.Drawing;
using System.Text.Json.Serialization;
using System.Windows;
+using Flow.Launcher.Infrastructure.Hotkey;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedModels;
using Flow.Launcher.ViewModel;
namespace Flow.Launcher.Infrastructure.UserSettings
{
- public class Settings : BaseModel
+ public class Settings : BaseModel, IHotkeySettings
{
private string language = "en";
private string _theme = Constant.DefaultTheme;
@@ -20,6 +21,16 @@ namespace Flow.Launcher.Infrastructure.UserSettings
public bool ShowOpenResultHotkey { get; set; } = true;
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 Language
{
@@ -54,6 +65,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings
public bool UseGlyphIcons { get; set; } = true;
public bool UseAnimation { get; set; } = true;
public bool UseSound { get; set; } = true;
+ public double SoundVolume { get; set; } = 50;
+
public bool UseClock { get; set; } = true;
public bool UseDate { get; set; } = false;
public string TimeFormat { get; set; } = "hh:mm tt";
@@ -195,17 +208,17 @@ namespace Flow.Launcher.Infrastructure.UserSettings
public double WindowLeft { get; set; }
public double WindowTop { get; set; }
-
+
///
/// Custom left position on selected monitor
///
public double CustomWindowLeft { get; set; } = 0;
-
+
///
/// Custom top position on selected monitor
///
public double CustomWindowTop { get; set; } = 0;
-
+
public int MaxResultsToShow { get; set; } = 5;
public int ActivateTimes { get; set; }
@@ -217,7 +230,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
[JsonIgnore]
public ObservableCollection BuiltinShortcuts { get; set; } = new()
{
- new BuiltinShortcutModel("{clipboard}", "shortcut_clipboard_description", Clipboard.GetText),
+ new BuiltinShortcutModel("{clipboard}", "shortcut_clipboard_description", Clipboard.GetText),
new BuiltinShortcutModel("{active_explorer_path}", "shortcut_active_explorer_path", FileExplorerHelper.GetActiveExplorerPath)
};
@@ -241,7 +254,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
[JsonConverter(typeof(JsonStringEnumConverter))]
public SearchWindowScreens SearchWindowScreen { get; set; } = SearchWindowScreens.Cursor;
-
+
[JsonConverter(typeof(JsonStringEnumConverter))]
public SearchWindowAligns SearchWindowAlign { get; set; } = SearchWindowAligns.Center;
@@ -261,6 +274,82 @@ namespace Flow.Launcher.Infrastructure.UserSettings
// This needs to be loaded last by staying at the bottom
public PluginsSettings PluginSettings { get; set; } = new PluginsSettings();
+
+ [JsonIgnore]
+ public List RegisteredHotkeys
+ {
+ get
+ {
+ var list = new List
+ {
+ new("Up", "HotkeyLeftRightDesc"),
+ new("Down", "HotkeyLeftRightDesc"),
+ new("Left", "HotkeyUpDownDesc"),
+ new("Right", "HotkeyUpDownDesc"),
+ new("Escape", "HotkeyESCDesc"),
+ new("F5", "ReloadPluginHotkey"),
+ new("Alt+Home", "HotkeySelectFirstResult"),
+ new("Alt+End", "HotkeySelectLastResult"),
+ new("Ctrl+R", "HotkeyRequery"),
+ new("Ctrl+H", "ToggleHistoryHotkey"),
+ new("Ctrl+OemCloseBrackets", "QuickWidthHotkey"),
+ new("Ctrl+OemOpenBrackets", "QuickWidthHotkey"),
+ new("Ctrl+OemPlus", "QuickHeightHotkey"),
+ new("Ctrl+OemMinus", "QuickHeightHotkey"),
+ new("Ctrl+Shift+Enter", "HotkeyCtrlShiftEnterDesc"),
+ new("Shift+Enter", "OpenContextMenuHotkey"),
+ new("Enter", "HotkeyRunDesc"),
+ new("Ctrl+Enter", "OpenContainFolderHotkey"),
+ new("Alt+Enter", "HotkeyOpenResult"),
+ new("Ctrl+F12", "ToggleGameModeHotkey"),
+ new("Ctrl+Shift+C", "CopyFilePathHotkey"),
+
+ new($"{OpenResultModifiers}+D1", "HotkeyOpenResultN", 1),
+ new($"{OpenResultModifiers}+D2", "HotkeyOpenResultN", 2),
+ new($"{OpenResultModifiers}+D3", "HotkeyOpenResultN", 3),
+ new($"{OpenResultModifiers}+D4", "HotkeyOpenResultN", 4),
+ new($"{OpenResultModifiers}+D5", "HotkeyOpenResultN", 5),
+ new($"{OpenResultModifiers}+D6", "HotkeyOpenResultN", 6),
+ new($"{OpenResultModifiers}+D7", "HotkeyOpenResultN", 7),
+ new($"{OpenResultModifiers}+D8", "HotkeyOpenResultN", 8),
+ new($"{OpenResultModifiers}+D9", "HotkeyOpenResultN", 9),
+ new($"{OpenResultModifiers}+D0", "HotkeyOpenResultN", 10)
+ };
+
+ if(!string.IsNullOrEmpty(Hotkey))
+ list.Add(new(Hotkey, "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(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 = ""));
+
+ foreach (var customPluginHotkey in CustomPluginHotkeys)
+ {
+ if (!string.IsNullOrEmpty(customPluginHotkey.Hotkey))
+ list.Add(new(customPluginHotkey.Hotkey, "customQueryHotkey", () => customPluginHotkey.Hotkey = ""));
+ }
+
+ return list;
+ }
+ }
}
public enum LastQueryMode
@@ -276,7 +365,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
Light,
Dark
}
-
+
public enum SearchWindowScreens
{
RememberLastLaunchLocation,
@@ -285,7 +374,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
Primary,
Custom
}
-
+
public enum SearchWindowAligns
{
Center,
diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
index a84a864f6..08ef1bb1b 100644
--- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
+++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
@@ -14,10 +14,10 @@
- 4.2.0
- 4.2.0
- 4.2.0
- 4.2.0
+ 4.3.0
+ 4.3.0
+ 4.3.0
+ 4.3.0
Flow.Launcher.Plugin
Flow-Launcher
MIT
diff --git a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs
index 49fe680f1..c95a8ce7b 100644
--- a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs
+++ b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs
@@ -291,5 +291,12 @@ namespace Flow.Launcher.Plugin
///
///
public bool IsGameModeOn();
+
+ ///
+ /// Reloads the query.
+ /// This method should run
+ ///
+ /// Choose the first result after reload if true; keep the last selected result if false. Default is true.
+ public void ReQuery(bool reselect = true);
}
}
diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs
index bfb0b0411..fc6c5d185 100644
--- a/Flow.Launcher.Plugin/Result.cs
+++ b/Flow.Launcher.Plugin/Result.cs
@@ -183,6 +183,31 @@ namespace Flow.Launcher.Plugin
return Title + SubTitle + Score;
}
+ ///
+ /// Clones the current result
+ ///
+ public Result Clone()
+ {
+ return new Result
+ {
+ Title = Title,
+ SubTitle = SubTitle,
+ ActionKeywordAssigned = ActionKeywordAssigned,
+ CopyText = CopyText,
+ AutoCompleteText = AutoCompleteText,
+ IcoPath = IcoPath,
+ RoundedIcon = RoundedIcon,
+ Icon = Icon,
+ Glyph = Glyph,
+ Action = Action,
+ AsyncAction = AsyncAction,
+ Score = Score,
+ TitleHighlightData = TitleHighlightData,
+ OriginQuery = OriginQuery,
+ PluginDirectory = PluginDirectory
+ };
+ }
+
///
/// Additional data associated with this result
///
diff --git a/Flow.Launcher/Converters/BoolToIMEConversionModeConverter.cs b/Flow.Launcher/Converters/BoolToIMEConversionModeConverter.cs
index ffb7d65b8..41e879913 100644
--- a/Flow.Launcher/Converters/BoolToIMEConversionModeConverter.cs
+++ b/Flow.Launcher/Converters/BoolToIMEConversionModeConverter.cs
@@ -3,39 +3,38 @@ using System.Globalization;
using System.Windows.Data;
using System.Windows.Input;
-namespace Flow.Launcher.Converters
-{
- internal class BoolToIMEConversionModeConverter : IValueConverter
- {
- public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
- {
- return value switch
- {
- true => ImeConversionModeValues.Alphanumeric,
- _ => ImeConversionModeValues.DoNotCare
- };
- }
+namespace Flow.Launcher.Converters;
- public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+internal class BoolToIMEConversionModeConverter : IValueConverter
+{
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return value switch
{
- throw new NotImplementedException();
- }
+ true => ImeConversionModeValues.Alphanumeric,
+ _ => ImeConversionModeValues.DoNotCare
+ };
}
- internal class BoolToIMEStateConverter : IValueConverter
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
- public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
- {
- return value switch
- {
- true => InputMethodState.Off,
- _ => InputMethodState.DoNotCare
- };
- }
-
- public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
- {
- throw new NotImplementedException();
- }
+ throw new NotImplementedException();
+ }
+}
+
+internal class BoolToIMEStateConverter : IValueConverter
+{
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return value switch
+ {
+ true => InputMethodState.Off,
+ _ => InputMethodState.DoNotCare
+ };
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
}
}
diff --git a/Flow.Launcher/Converters/BoolToVisibilityConverter.cs b/Flow.Launcher/Converters/BoolToVisibilityConverter.cs
index ac3b2cfd0..c0fe6ab55 100644
--- a/Flow.Launcher/Converters/BoolToVisibilityConverter.cs
+++ b/Flow.Launcher/Converters/BoolToVisibilityConverter.cs
@@ -2,40 +2,39 @@
using System.Windows;
using System.Windows.Data;
-namespace Flow.Launcher.Converters
+namespace Flow.Launcher.Converters;
+
+public class BoolToVisibilityConverter : IValueConverter
{
- public class BoolToVisibilityConverter : IValueConverter
+ public object Convert(object value, System.Type targetType, object parameter, CultureInfo culture)
{
- public object Convert(object value, System.Type targetType, object parameter, CultureInfo culture)
+ return (value, parameter) switch
{
- return (value, parameter) switch
- {
- (true, not null) => Visibility.Collapsed,
- (_, not null) => Visibility.Visible,
+ (true, not null) => Visibility.Collapsed,
+ (_, not null) => Visibility.Visible,
- (true, null) => Visibility.Visible,
- (_, null) => Visibility.Collapsed
- };
- }
-
- public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException();
+ (true, null) => Visibility.Visible,
+ (_, null) => Visibility.Collapsed
+ };
}
- public class SplitterConverter : IValueConverter
- /* Prevents the dragging part of the preview area from working when preview is turned off. */
- {
- public object Convert(object value, System.Type targetType, object parameter, CultureInfo culture)
- {
- return (value, parameter) switch
- {
- (true, not null) => 0,
- (_, not null) => 5,
-
- (true, null) => 5,
- (_, null) => 0
- };
- }
-
- public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException();
- }
+ public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException();
+}
+
+public class SplitterConverter : IValueConverter
+/* Prevents the dragging part of the preview area from working when preview is turned off. */
+{
+ public object Convert(object value, System.Type targetType, object parameter, CultureInfo culture)
+ {
+ return (value, parameter) switch
+ {
+ (true, not null) => 0,
+ (_, not null) => 5,
+
+ (true, null) => 5,
+ (_, null) => 0
+ };
+ }
+
+ public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException();
}
diff --git a/Flow.Launcher/Converters/BorderClipConverter.cs b/Flow.Launcher/Converters/BorderClipConverter.cs
index a8d9b34c6..9b0579c33 100644
--- a/Flow.Launcher/Converters/BorderClipConverter.cs
+++ b/Flow.Launcher/Converters/BorderClipConverter.cs
@@ -7,43 +7,41 @@ using System.Windows.Shapes;
// For Clipping inside listbox item
-namespace Flow.Launcher.Converters
+namespace Flow.Launcher.Converters;
+
+public class BorderClipConverter : IMultiValueConverter
{
- public class BorderClipConverter : IMultiValueConverter
+ public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
- public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+ if (values is not [double width, double height, CornerRadius radius])
{
- if (values is not [double width, double height, CornerRadius radius])
- {
- return DependencyProperty.UnsetValue;
- }
-
- Path myPath = new Path();
- if (width < Double.Epsilon || height < Double.Epsilon)
- {
- return Geometry.Empty;
- }
- var radiusHeight = radius.TopLeft;
-
- // Drawing Round box for bottom round, and rect for top area of listbox.
- var corner = new RectangleGeometry(new Rect(0, 0, width, height), radius.TopLeft, radius.TopLeft);
- var box = new RectangleGeometry(new Rect(0, 0, width, radiusHeight), 0, 0);
-
- GeometryGroup myGeometryGroup = new GeometryGroup();
- myGeometryGroup.Children.Add(corner);
- myGeometryGroup.Children.Add(box);
-
- CombinedGeometry c1 = new CombinedGeometry(GeometryCombineMode.Union, corner, box);
- myPath.Data = c1;
-
- myPath.Data.Freeze();
- return myPath.Data;
+ return DependencyProperty.UnsetValue;
}
- public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
+ Path myPath = new Path();
+ if (width < Double.Epsilon || height < Double.Epsilon)
{
- throw new NotSupportedException();
+ return Geometry.Empty;
}
+ var radiusHeight = radius.TopLeft;
+
+ // Drawing Round box for bottom round, and rect for top area of listbox.
+ var corner = new RectangleGeometry(new Rect(0, 0, width, height), radius.TopLeft, radius.TopLeft);
+ var box = new RectangleGeometry(new Rect(0, 0, width, radiusHeight), 0, 0);
+
+ GeometryGroup myGeometryGroup = new GeometryGroup();
+ myGeometryGroup.Children.Add(corner);
+ myGeometryGroup.Children.Add(box);
+
+ CombinedGeometry c1 = new CombinedGeometry(GeometryCombineMode.Union, corner, box);
+ myPath.Data = c1;
+
+ myPath.Data.Freeze();
+ return myPath.Data;
}
+ public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
+ {
+ throw new NotSupportedException();
+ }
}
diff --git a/Flow.Launcher/Converters/DateTimeFormatToNowConverter.cs b/Flow.Launcher/Converters/DateTimeFormatToNowConverter.cs
index 3c46fd01a..7d75ffd5c 100644
--- a/Flow.Launcher/Converters/DateTimeFormatToNowConverter.cs
+++ b/Flow.Launcher/Converters/DateTimeFormatToNowConverter.cs
@@ -2,18 +2,17 @@
using System.Globalization;
using System.Windows.Data;
-namespace Flow.Launcher.Converters
-{
- public class DateTimeFormatToNowConverter : IValueConverter
- {
+namespace Flow.Launcher.Converters;
- public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
- {
- return value is not string format ? null : DateTime.Now.ToString(format);
- }
- public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
- {
- throw new NotImplementedException();
- }
+public class DateTimeFormatToNowConverter : IValueConverter
+{
+
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return value is not string format ? null : DateTime.Now.ToString(format);
+ }
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
}
}
diff --git a/Flow.Launcher/Converters/DiameterToCenterPointConverter.cs b/Flow.Launcher/Converters/DiameterToCenterPointConverter.cs
index e81bb2507..24a316603 100644
--- a/Flow.Launcher/Converters/DiameterToCenterPointConverter.cs
+++ b/Flow.Launcher/Converters/DiameterToCenterPointConverter.cs
@@ -3,23 +3,22 @@ using System.Globalization;
using System.Windows;
using System.Windows.Data;
-namespace Flow.Launcher.Converters
+namespace Flow.Launcher.Converters;
+
+public class DiameterToCenterPointConverter : IValueConverter
{
- public class DiameterToCenterPointConverter : IValueConverter
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
- public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ if (value is double d)
{
- if (value is double d)
- {
- return new Point(d / 2, d / 2);
- }
-
- return new Point(0, 0);
+ return new Point(d / 2, d / 2);
}
- public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
- {
- throw new NotSupportedException();
- }
+ return new Point(0, 0);
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotSupportedException();
}
}
diff --git a/Flow.Launcher/Converters/HighlightTextConverter.cs b/Flow.Launcher/Converters/HighlightTextConverter.cs
index 3f02443e2..436f5fed5 100644
--- a/Flow.Launcher/Converters/HighlightTextConverter.cs
+++ b/Flow.Launcher/Converters/HighlightTextConverter.cs
@@ -5,45 +5,44 @@ using System.Windows;
using System.Windows.Data;
using System.Windows.Documents;
-namespace Flow.Launcher.Converters
+namespace Flow.Launcher.Converters;
+
+public class HighlightTextConverter : IMultiValueConverter
{
- public class HighlightTextConverter : IMultiValueConverter
+ public object Convert(object[] value, Type targetType, object parameter, CultureInfo cultureInfo)
{
- public object Convert(object[] value, Type targetType, object parameter, CultureInfo cultureInfo)
- {
- if (value.Length < 2)
- return new Run(string.Empty);
-
- if (value[0] is not string text)
- return new Run(string.Empty);
-
- if (value[1] is not List { Count: > 0 } highlightData)
- // No highlight data, just return the text
- return new Run(text);
-
- var highlightStyle = (Style)Application.Current.FindResource("HighlightStyle");
- var textBlock = new Span();
+ if (value.Length < 2)
+ return new Run(string.Empty);
- for (var i = 0; i < text.Length; i++)
+ if (value[0] is not string text)
+ return new Run(string.Empty);
+
+ if (value[1] is not List { Count: > 0 } highlightData)
+ // No highlight data, just return the text
+ return new Run(text);
+
+ var highlightStyle = (Style)Application.Current.FindResource("HighlightStyle");
+ var textBlock = new Span();
+
+ for (var i = 0; i < text.Length; i++)
+ {
+ var currentCharacter = text.Substring(i, 1);
+ var run = new Run(currentCharacter)
{
- var currentCharacter = text.Substring(i, 1);
- var run = new Run(currentCharacter)
- {
- Style = ShouldHighlight(highlightData, i) ? highlightStyle : null
- };
- textBlock.Inlines.Add(run);
- }
- return textBlock;
+ Style = ShouldHighlight(highlightData, i) ? highlightStyle : null
+ };
+ textBlock.Inlines.Add(run);
}
+ return textBlock;
+ }
- public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
- {
- return new[] { DependencyProperty.UnsetValue, DependencyProperty.UnsetValue };
- }
+ public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
+ {
+ return new[] { DependencyProperty.UnsetValue, DependencyProperty.UnsetValue };
+ }
- private bool ShouldHighlight(List highlightData, int index)
- {
- return highlightData.Contains(index);
- }
+ private bool ShouldHighlight(List highlightData, int index)
+ {
+ return highlightData.Contains(index);
}
}
diff --git a/Flow.Launcher/Converters/IconRadiusConverter.cs b/Flow.Launcher/Converters/IconRadiusConverter.cs
index d0bdc9d1c..d26b39d6b 100644
--- a/Flow.Launcher/Converters/IconRadiusConverter.cs
+++ b/Flow.Launcher/Converters/IconRadiusConverter.cs
@@ -2,20 +2,19 @@
using System.Globalization;
using System.Windows.Data;
-namespace Flow.Launcher.Converters
-{
- public class IconRadiusConverter : IMultiValueConverter
- {
- public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
- {
- if (values is not [double size, bool isIconCircular])
- throw new ArgumentException("IconRadiusConverter must have 2 parameters: [double, bool]");
+namespace Flow.Launcher.Converters;
- return isIconCircular ? size / 2 : size;
- }
- public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
- {
- throw new NotSupportedException();
- }
+public class IconRadiusConverter : IMultiValueConverter
+{
+ public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (values is not [double size, bool isIconCircular])
+ throw new ArgumentException("IconRadiusConverter must have 2 parameters: [double, bool]");
+
+ return isIconCircular ? size / 2 : size;
+ }
+ public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
+ {
+ throw new NotSupportedException();
}
}
diff --git a/Flow.Launcher/Converters/OpenResultHotkeyVisibilityConverter.cs b/Flow.Launcher/Converters/OpenResultHotkeyVisibilityConverter.cs
index 7b7bd0906..7ab13190a 100644
--- a/Flow.Launcher/Converters/OpenResultHotkeyVisibilityConverter.cs
+++ b/Flow.Launcher/Converters/OpenResultHotkeyVisibilityConverter.cs
@@ -4,24 +4,23 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
-namespace Flow.Launcher.Converters
+namespace Flow.Launcher.Converters;
+
+[ValueConversion(typeof(bool), typeof(Visibility))]
+public class OpenResultHotkeyVisibilityConverter : IValueConverter
{
- [ValueConversion(typeof(bool), typeof(Visibility))]
- public class OpenResultHotkeyVisibilityConverter : IValueConverter
+ private const int MaxVisibleHotkeys = 10;
+
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
- private const int MaxVisibleHotkeys = 10;
+ var number = int.MaxValue;
- public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
- {
- var number = int.MaxValue;
+ if (value is ListBoxItem listBoxItem
+ && ItemsControl.ItemsControlFromItemContainer(listBoxItem) is ListBox listBox)
+ number = listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1;
- if (value is ListBoxItem listBoxItem
- && ItemsControl.ItemsControlFromItemContainer(listBoxItem) is ListBox listBox)
- number = listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1;
-
- return number <= MaxVisibleHotkeys ? Visibility.Visible : Visibility.Collapsed;
- }
-
- public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException();
+ return number <= MaxVisibleHotkeys ? Visibility.Visible : Visibility.Collapsed;
}
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException();
}
diff --git a/Flow.Launcher/Converters/OrdinalConverter.cs b/Flow.Launcher/Converters/OrdinalConverter.cs
index 438d4effa..9aed2e07b 100644
--- a/Flow.Launcher/Converters/OrdinalConverter.cs
+++ b/Flow.Launcher/Converters/OrdinalConverter.cs
@@ -3,23 +3,22 @@ using System.Globalization;
using System.Windows.Controls;
using System.Windows.Data;
-namespace Flow.Launcher.Converters
+namespace Flow.Launcher.Converters;
+
+public class OrdinalConverter : IValueConverter
{
- public class OrdinalConverter : IValueConverter
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
- public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ if (value is not ListBoxItem listBoxItem
+ || ItemsControl.ItemsControlFromItemContainer(listBoxItem) is not ListBox listBox)
{
- if (value is not ListBoxItem listBoxItem
- || ItemsControl.ItemsControlFromItemContainer(listBoxItem) is not ListBox listBox)
- {
- return 0;
- }
-
- var res = listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1;
- return res == 10 ? 0 : res; // 10th item => HOTKEY+0
-
+ return 0;
}
- public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException();
+ var res = listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1;
+ return res == 10 ? 0 : res; // 10th item => HOTKEY+0
+
}
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException();
}
diff --git a/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs b/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs
index 4a8c9e92b..fabd01a24 100644
--- a/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs
+++ b/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs
@@ -6,63 +6,62 @@ using System.Windows.Media;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.ViewModel;
-namespace Flow.Launcher.Converters
+namespace Flow.Launcher.Converters;
+
+public class QuerySuggestionBoxConverter : IMultiValueConverter
{
- public class QuerySuggestionBoxConverter : IMultiValueConverter
+ public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
- public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+ // values[0] is TextBox: The textbox displaying the autocomplete suggestion
+ // values[1] is ResultViewModel: Currently selected item in the list
+ // values[2] is string: Query text
+ if (
+ values.Length != 3 ||
+ values[0] is not TextBox queryTextBox ||
+ values[1] is null ||
+ values[2] is not string queryText ||
+ string.IsNullOrEmpty(queryText)
+ )
+ return string.Empty;
+
+ if (values[1] is not ResultViewModel selectedItem)
+ return Binding.DoNothing;
+
+ try
{
- // values[0] is TextBox: The textbox displaying the autocomplete suggestion
- // values[1] is ResultViewModel: Currently selected item in the list
- // values[2] is string: Query text
- if (
- values.Length != 3 ||
- values[0] is not TextBox queryTextBox ||
- values[1] is null ||
- values[2] is not string queryText ||
- string.IsNullOrEmpty(queryText)
- )
+ var selectedResult = selectedItem.Result;
+ var selectedResultActionKeyword = string.IsNullOrEmpty(selectedResult.ActionKeywordAssigned) ? "" : selectedResult.ActionKeywordAssigned + " ";
+ var selectedResultPossibleSuggestion = selectedResultActionKeyword + selectedResult.Title;
+
+ if (!selectedResultPossibleSuggestion.StartsWith(queryText, StringComparison.CurrentCultureIgnoreCase))
return string.Empty;
-
- if (values[1] is not ResultViewModel selectedItem)
- return Binding.DoNothing;
-
- try
- {
- var selectedResult = selectedItem.Result;
- var selectedResultActionKeyword = string.IsNullOrEmpty(selectedResult.ActionKeywordAssigned) ? "" : selectedResult.ActionKeywordAssigned + " ";
- var selectedResultPossibleSuggestion = selectedResultActionKeyword + selectedResult.Title;
-
- if (!selectedResultPossibleSuggestion.StartsWith(queryText, StringComparison.CurrentCultureIgnoreCase))
- return string.Empty;
- // For AutocompleteQueryCommand.
- // When user typed lower case and result title is uppercase, we still want to display suggestion
- selectedItem.QuerySuggestionText = queryText + selectedResultPossibleSuggestion.Substring(queryText.Length);
+ // For AutocompleteQueryCommand.
+ // When user typed lower case and result title is uppercase, we still want to display suggestion
+ selectedItem.QuerySuggestionText = queryText + selectedResultPossibleSuggestion.Substring(queryText.Length);
- // Check if Text will be larger than our QueryTextBox
- Typeface typeface = new Typeface(queryTextBox.FontFamily, queryTextBox.FontStyle, queryTextBox.FontWeight, queryTextBox.FontStretch);
- // TODO: Obsolete warning?
- var ft = new FormattedText(queryTextBox.Text, CultureInfo.DefaultThreadCurrentCulture, System.Windows.FlowDirection.LeftToRight, typeface, queryTextBox.FontSize, Brushes.Black);
+ // Check if Text will be larger than our QueryTextBox
+ Typeface typeface = new Typeface(queryTextBox.FontFamily, queryTextBox.FontStyle, queryTextBox.FontWeight, queryTextBox.FontStretch);
+ // TODO: Obsolete warning?
+ var ft = new FormattedText(queryTextBox.Text, CultureInfo.DefaultThreadCurrentCulture, System.Windows.FlowDirection.LeftToRight, typeface, queryTextBox.FontSize, Brushes.Black);
- var offset = queryTextBox.Padding.Right;
+ var offset = queryTextBox.Padding.Right;
- if (ft.Width + offset > queryTextBox.ActualWidth || queryTextBox.HorizontalOffset != 0)
- return string.Empty;
-
- return selectedItem.QuerySuggestionText;
- }
- catch (Exception e)
- {
- Log.Exception(nameof(QuerySuggestionBoxConverter), "fail to convert text for suggestion box", e);
+ if (ft.Width + offset > queryTextBox.ActualWidth || queryTextBox.HorizontalOffset != 0)
return string.Empty;
- }
+
+ return selectedItem.QuerySuggestionText;
}
-
- public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
+ catch (Exception e)
{
- throw new NotImplementedException();
+ Log.Exception(nameof(QuerySuggestionBoxConverter), "fail to convert text for suggestion box", e);
+ return string.Empty;
}
}
+
+ public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
}
diff --git a/Flow.Launcher/Converters/StringToKeyBindingConverter.cs b/Flow.Launcher/Converters/StringToKeyBindingConverter.cs
index 22526bd82..21bf584e7 100644
--- a/Flow.Launcher/Converters/StringToKeyBindingConverter.cs
+++ b/Flow.Launcher/Converters/StringToKeyBindingConverter.cs
@@ -3,28 +3,27 @@ using System.Globalization;
using System.Windows.Data;
using System.Windows.Input;
-namespace Flow.Launcher.Converters
-{
- class StringToKeyBindingConverter : IValueConverter
- {
- public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
- {
- if (parameter is not string mode || value is not string hotkeyStr)
- return null;
-
- var converter = new KeyGestureConverter();
- var key = (KeyGesture)converter.ConvertFromString(hotkeyStr);
- return mode switch
- {
- "key" => key?.Key,
- "modifiers" => key?.Modifiers,
- _ => null
- };
- }
+namespace Flow.Launcher.Converters;
- public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+class StringToKeyBindingConverter : IValueConverter
+{
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (parameter is not string mode || value is not string hotkeyStr)
+ return null;
+
+ var converter = new KeyGestureConverter();
+ var key = (KeyGesture)converter.ConvertFromString(hotkeyStr);
+ return mode switch
{
- throw new NotImplementedException();
- }
+ "key" => key?.Key,
+ "modifiers" => key?.Modifiers,
+ _ => null
+ };
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
}
}
diff --git a/Flow.Launcher/Converters/TextConverter.cs b/Flow.Launcher/Converters/TextConverter.cs
index c0b34d053..5f0e1ea82 100644
--- a/Flow.Launcher/Converters/TextConverter.cs
+++ b/Flow.Launcher/Converters/TextConverter.cs
@@ -4,28 +4,27 @@ using System.Windows.Data;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.ViewModel;
-namespace Flow.Launcher.Converters
-{
- public class TextConverter : IValueConverter
- {
- public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
- {
- var id = value?.ToString();
- var translationKey = id switch
- {
- PluginStoreItemViewModel.NewRelease => "pluginStore_NewRelease",
- PluginStoreItemViewModel.RecentlyUpdated => "pluginStore_RecentlyUpdated",
- PluginStoreItemViewModel.None => "pluginStore_None",
- PluginStoreItemViewModel.Installed => "pluginStore_Installed",
- _ => null
- };
-
- if (translationKey is null)
- return id;
-
- return InternationalizationManager.Instance.GetTranslation(translationKey);
- }
+namespace Flow.Launcher.Converters;
- public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException();
+public class TextConverter : IValueConverter
+{
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ var id = value?.ToString();
+ var translationKey = id switch
+ {
+ PluginStoreItemViewModel.NewRelease => "pluginStore_NewRelease",
+ PluginStoreItemViewModel.RecentlyUpdated => "pluginStore_RecentlyUpdated",
+ PluginStoreItemViewModel.None => "pluginStore_None",
+ PluginStoreItemViewModel.Installed => "pluginStore_Installed",
+ _ => null
+ };
+
+ if (translationKey is null)
+ return id;
+
+ return InternationalizationManager.Instance.GetTranslation(translationKey);
}
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException();
}
diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml b/Flow.Launcher/CustomQueryHotkeySetting.xaml
index 1113cb24d..068afda15 100644
--- a/Flow.Launcher/CustomQueryHotkeySetting.xaml
+++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml
@@ -2,10 +2,12 @@
x:Class="Flow.Launcher.CustomQueryHotkeySetting"
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:flowlauncher="clr-namespace:Flow.Launcher"
Title="{DynamicResource customeQueryHotkeyTitle}"
Width="530"
Background="{DynamicResource PopuBGColor}"
+ DataContext="{Binding RelativeSource={RelativeSource Self}}"
Foreground="{DynamicResource PopupTextColor}"
Icon="Images\app.png"
MouseDown="window_MouseDown"
@@ -60,94 +62,76 @@
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs
index 110beeded..5db6115ad 100644
--- a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs
+++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Controls;
+using Flow.Launcher.ViewModel;
namespace Flow.Launcher
{
@@ -14,13 +15,13 @@ namespace Flow.Launcher
private SettingWindow _settingWidow;
private bool update;
private CustomPluginHotkey updateCustomHotkey;
- private Settings _settings;
+ public Settings Settings { get; }
public CustomQueryHotkeySetting(SettingWindow settingWidow, Settings settings)
{
_settingWidow = settingWidow;
+ Settings = settings;
InitializeComponent();
- _settings = settings;
}
private void BtnCancel_OnClick(object sender, RoutedEventArgs e)
@@ -32,36 +33,21 @@ namespace Flow.Launcher
{
if (!update)
{
- if (!ctlHotkey.CurrentHotkeyAvailable)
- {
- MessageBox.Show(InternationalizationManager.Instance.GetTranslation("hotkeyIsNotUnavailable"));
- return;
- }
-
- if (_settings.CustomPluginHotkeys == null)
- {
- _settings.CustomPluginHotkeys = new ObservableCollection();
- }
+ Settings.CustomPluginHotkeys ??= new ObservableCollection();
var pluginHotkey = new CustomPluginHotkey
{
- Hotkey = ctlHotkey.CurrentHotkey.ToString(),
- ActionKeyword = tbAction.Text
+ Hotkey = HotkeyControl.CurrentHotkey.ToString(), ActionKeyword = tbAction.Text
};
- _settings.CustomPluginHotkeys.Add(pluginHotkey);
+ Settings.CustomPluginHotkeys.Add(pluginHotkey);
HotKeyMapper.SetCustomQueryHotkey(pluginHotkey);
}
else
{
- if (updateCustomHotkey.Hotkey != ctlHotkey.CurrentHotkey.ToString() && !ctlHotkey.CurrentHotkeyAvailable)
- {
- MessageBox.Show(InternationalizationManager.Instance.GetTranslation("hotkeyIsNotUnavailable"));
- return;
- }
var oldHotkey = updateCustomHotkey.Hotkey;
updateCustomHotkey.ActionKeyword = tbAction.Text;
- updateCustomHotkey.Hotkey = ctlHotkey.CurrentHotkey.ToString();
+ updateCustomHotkey.Hotkey = HotkeyControl.CurrentHotkey.ToString();
//remove origin hotkey
HotKeyMapper.RemoveHotkey(oldHotkey);
HotKeyMapper.SetCustomQueryHotkey(updateCustomHotkey);
@@ -70,9 +56,11 @@ namespace Flow.Launcher
Close();
}
+
public void UpdateItem(CustomPluginHotkey item)
{
- updateCustomHotkey = _settings.CustomPluginHotkeys.FirstOrDefault(o => o.ActionKeyword == item.ActionKeyword && o.Hotkey == item.Hotkey);
+ updateCustomHotkey = Settings.CustomPluginHotkeys.FirstOrDefault(o =>
+ o.ActionKeyword == item.ActionKeyword && o.Hotkey == item.Hotkey);
if (updateCustomHotkey == null)
{
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("invalidPluginHotkey"));
@@ -81,7 +69,7 @@ namespace Flow.Launcher
}
tbAction.Text = updateCustomHotkey.ActionKeyword;
- _ = ctlHotkey.SetHotkeyAsync(updateCustomHotkey.Hotkey, false);
+ HotkeyControl.SetHotkey(updateCustomHotkey.Hotkey, false);
update = true;
lblAdd.Text = InternationalizationManager.Instance.GetTranslation("update");
}
@@ -101,12 +89,10 @@ namespace Flow.Launcher
private void window_MouseDown(object sender, MouseButtonEventArgs e) /* for close hotkey popup */
{
- TextBox textBox = Keyboard.FocusedElement as TextBox;
- if (textBox != null)
- {
- TraversalRequest tRequest = new TraversalRequest(FocusNavigationDirection.Next);
- textBox.MoveFocus(tRequest);
- }
+ if (Keyboard.FocusedElement is not TextBox textBox) return;
+
+ TraversalRequest tRequest = new TraversalRequest(FocusNavigationDirection.Next);
+ textBox.MoveFocus(tRequest);
}
}
}
diff --git a/Flow.Launcher/Helper/AutoStartup.cs b/Flow.Launcher/Helper/AutoStartup.cs
index bf36b7f6f..4bff30caf 100644
--- a/Flow.Launcher/Helper/AutoStartup.cs
+++ b/Flow.Launcher/Helper/AutoStartup.cs
@@ -3,57 +3,56 @@ using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Logger;
using Microsoft.Win32;
-namespace Flow.Launcher.Helper
+namespace Flow.Launcher.Helper;
+
+public class AutoStartup
{
- public class AutoStartup
+ private const string StartupPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
+
+ public static bool IsEnabled
{
- private const string StartupPath = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
-
- public static bool IsEnabled
- {
- get
- {
- try
- {
- using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
- var path = key?.GetValue(Constant.FlowLauncher) as string;
- return path == Constant.ExecutablePath;
- }
- catch (Exception e)
- {
- Log.Error("AutoStartup", $"Ignoring non-critical registry error (querying if enabled): {e}");
- }
-
- return false;
- }
- }
-
- public static void Disable()
+ get
{
try
{
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
- key?.DeleteValue(Constant.FlowLauncher, false);
+ var path = key?.GetValue(Constant.FlowLauncher) as string;
+ return path == Constant.ExecutablePath;
}
catch (Exception e)
{
- Log.Error("AutoStartup", $"Failed to disable auto-startup: {e}");
- throw;
+ Log.Error("AutoStartup", $"Ignoring non-critical registry error (querying if enabled): {e}");
}
- }
- internal static void Enable()
+ return false;
+ }
+ }
+
+ public static void Disable()
+ {
+ try
{
- try
- {
- using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
- key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\"");
- }
- catch (Exception e)
- {
- Log.Error("AutoStartup", $"Failed to enable auto-startup: {e}");
- throw;
- }
+ using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
+ key?.DeleteValue(Constant.FlowLauncher, false);
+ }
+ catch (Exception e)
+ {
+ Log.Error("AutoStartup", $"Failed to disable auto-startup: {e}");
+ throw;
+ }
+ }
+
+ internal static void Enable()
+ {
+ try
+ {
+ using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
+ key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\"");
+ }
+ catch (Exception e)
+ {
+ Log.Error("AutoStartup", $"Failed to enable auto-startup: {e}");
+ throw;
}
}
}
diff --git a/Flow.Launcher/Helper/DWMDropShadow.cs b/Flow.Launcher/Helper/DWMDropShadow.cs
index 3a1f82d6f..e448acd4c 100644
--- a/Flow.Launcher/Helper/DWMDropShadow.cs
+++ b/Flow.Launcher/Helper/DWMDropShadow.cs
@@ -4,70 +4,69 @@ using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
-namespace Flow.Launcher.Helper
+namespace Flow.Launcher.Helper;
+
+public class DwmDropShadow
{
- public class DwmDropShadow
+
+ [DllImport("dwmapi.dll", PreserveSig = true)]
+ private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
+
+ [DllImport("dwmapi.dll")]
+ private static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref Margins pMarInset);
+
+ ///
+ /// Drops a standard shadow to a WPF Window, even if the window isborderless. Only works with DWM (Vista and Seven).
+ /// This method is much more efficient than setting AllowsTransparency to true and using the DropShadow effect,
+ /// as AllowsTransparency involves a huge permormance issue (hardware acceleration is turned off for all the window).
+ ///
+ /// Window to which the shadow will be applied
+ public static void DropShadowToWindow(Window window)
{
-
- [DllImport("dwmapi.dll", PreserveSig = true)]
- private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
-
- [DllImport("dwmapi.dll")]
- private static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref Margins pMarInset);
-
- ///
- /// Drops a standard shadow to a WPF Window, even if the window isborderless. Only works with DWM (Vista and Seven).
- /// This method is much more efficient than setting AllowsTransparency to true and using the DropShadow effect,
- /// as AllowsTransparency involves a huge permormance issue (hardware acceleration is turned off for all the window).
- ///
- /// Window to which the shadow will be applied
- public static void DropShadowToWindow(Window window)
+ if (!DropShadow(window))
{
- if (!DropShadow(window))
- {
- window.SourceInitialized += window_SourceInitialized;
- }
+ window.SourceInitialized += window_SourceInitialized;
}
+ }
- private static void window_SourceInitialized(object sender, EventArgs e) //fixed typo
+ private static void window_SourceInitialized(object sender, EventArgs e) //fixed typo
+ {
+ Window window = (Window)sender;
+
+ DropShadow(window);
+
+ window.SourceInitialized -= window_SourceInitialized;
+ }
+
+ ///
+ /// The actual method that makes API calls to drop the shadow to the window
+ ///
+ /// Window to which the shadow will be applied
+ /// True if the method succeeded, false if not
+ private static bool DropShadow(Window window)
+ {
+ try
{
- Window window = (Window)sender;
+ WindowInteropHelper helper = new WindowInteropHelper(window);
+ int val = 2;
+ int ret1 = DwmSetWindowAttribute(helper.Handle, 2, ref val, 4);
- DropShadow(window);
-
- window.SourceInitialized -= window_SourceInitialized;
- }
-
- ///
- /// The actual method that makes API calls to drop the shadow to the window
- ///
- /// Window to which the shadow will be applied
- /// True if the method succeeded, false if not
- private static bool DropShadow(Window window)
- {
- try
+ if (ret1 == 0)
{
- WindowInteropHelper helper = new WindowInteropHelper(window);
- int val = 2;
- int ret1 = DwmSetWindowAttribute(helper.Handle, 2, ref val, 4);
-
- if (ret1 == 0)
- {
- Margins m = new Margins { Bottom = 0, Left = 0, Right = 0, Top = 0 };
- int ret2 = DwmExtendFrameIntoClientArea(helper.Handle, ref m);
- return ret2 == 0;
- }
- else
- {
- return false;
- }
+ Margins m = new Margins { Bottom = 0, Left = 0, Right = 0, Top = 0 };
+ int ret2 = DwmExtendFrameIntoClientArea(helper.Handle, ref m);
+ return ret2 == 0;
}
- catch (Exception)
+ else
{
- // Probably dwmapi.dll not found (incompatible OS)
return false;
}
}
-
+ catch (Exception)
+ {
+ // Probably dwmapi.dll not found (incompatible OS)
+ return false;
+ }
}
-}
\ No newline at end of file
+
+}
diff --git a/Flow.Launcher/Helper/DataWebRequestFactory.cs b/Flow.Launcher/Helper/DataWebRequestFactory.cs
index b3b7e9c63..2e72ee240 100644
--- a/Flow.Launcher/Helper/DataWebRequestFactory.cs
+++ b/Flow.Launcher/Helper/DataWebRequestFactory.cs
@@ -2,68 +2,67 @@
using System.IO;
using System.Net;
-namespace Flow.Launcher.Helper
+namespace Flow.Launcher.Helper;
+
+public class DataWebRequestFactory : IWebRequestCreate
{
- public class DataWebRequestFactory : IWebRequestCreate
+ class DataWebRequest : WebRequest
{
- class DataWebRequest : WebRequest
+ private readonly Uri _uri;
+
+ public DataWebRequest(Uri uri)
{
- private readonly Uri m_uri;
-
- public DataWebRequest(Uri uri)
- {
- m_uri = uri;
- }
-
- public override WebResponse GetResponse()
- {
- return new DataWebResponse(m_uri);
- }
+ _uri = uri;
}
- class DataWebResponse : WebResponse
+ public override WebResponse GetResponse()
{
- private readonly string m_contentType;
- private readonly byte[] m_data;
-
- public DataWebResponse(Uri uri)
- {
- string uriString = uri.AbsoluteUri;
-
- int commaIndex = uriString.IndexOf(',');
- var headers = uriString.Substring(0, commaIndex).Split(';');
- m_contentType = headers[0];
- string dataString = uriString.Substring(commaIndex + 1);
- m_data = Convert.FromBase64String(dataString);
- }
-
- public override string ContentType
- {
- get { return m_contentType; }
- set
- {
- throw new NotSupportedException();
- }
- }
-
- public override long ContentLength
- {
- get { return m_data.Length; }
- set
- {
- throw new NotSupportedException();
- }
- }
-
- public override Stream GetResponseStream()
- {
- return new MemoryStream(m_data);
- }
- }
-
- public WebRequest Create(Uri uri)
- {
- return new DataWebRequest(uri);
+ return new DataWebResponse(_uri);
}
}
+
+ class DataWebResponse : WebResponse
+ {
+ private readonly string _contentType;
+ private readonly byte[] _data;
+
+ public DataWebResponse(Uri uri)
+ {
+ string uriString = uri.AbsoluteUri;
+
+ int commaIndex = uriString.IndexOf(',');
+ var headers = uriString.Substring(0, commaIndex).Split(';');
+ _contentType = headers[0];
+ string dataString = uriString.Substring(commaIndex + 1);
+ _data = Convert.FromBase64String(dataString);
+ }
+
+ public override string ContentType
+ {
+ get { return _contentType; }
+ set
+ {
+ throw new NotSupportedException();
+ }
+ }
+
+ public override long ContentLength
+ {
+ get { return _data.Length; }
+ set
+ {
+ throw new NotSupportedException();
+ }
+ }
+
+ public override Stream GetResponseStream()
+ {
+ return new MemoryStream(_data);
+ }
+ }
+
+ public WebRequest Create(Uri uri)
+ {
+ return new DataWebRequest(uri);
+ }
}
diff --git a/Flow.Launcher/Helper/ErrorReporting.cs b/Flow.Launcher/Helper/ErrorReporting.cs
index b5da2efad..5b79c520d 100644
--- a/Flow.Launcher/Helper/ErrorReporting.cs
+++ b/Flow.Launcher/Helper/ErrorReporting.cs
@@ -4,45 +4,52 @@ using NLog;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Exception;
-namespace Flow.Launcher.Helper
+namespace Flow.Launcher.Helper;
+
+public static class ErrorReporting
{
- public static class ErrorReporting
+ private static void Report(Exception e)
{
- private static void Report(Exception e)
- {
- var logger = LogManager.GetLogger("UnHandledException");
- logger.Fatal(ExceptionFormatter.FormatExcpetion(e));
- var reportWindow = new ReportWindow(e);
- reportWindow.Show();
- }
+ var logger = LogManager.GetLogger("UnHandledException");
+ logger.Fatal(ExceptionFormatter.FormatExcpetion(e));
+ var reportWindow = new ReportWindow(e);
+ reportWindow.Show();
+ }
- public static void UnhandledExceptionHandle(object sender, UnhandledExceptionEventArgs e)
- {
- //handle non-ui thread exceptions
- Report((Exception)e.ExceptionObject);
- }
+ public static void UnhandledExceptionHandle(object sender, UnhandledExceptionEventArgs e)
+ {
+ //handle non-ui thread exceptions
+ Report((Exception)e.ExceptionObject);
+ }
- public static void DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
- {
- //handle ui thread exceptions
- Report(e.Exception);
- //prevent application exist, so the user can copy prompted error info
- e.Handled = true;
- }
-
- public static string RuntimeInfo()
- {
- var info = $"\nFlow Launcher version: {Constant.Version}" +
- $"\nOS Version: {ExceptionFormatter.GetWindowsFullVersionFromRegistry()}" +
- $"\nIntPtr Length: {IntPtr.Size}" +
- $"\nx64: {Environment.Is64BitOperatingSystem}";
- return info;
- }
+ public static void DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
+ {
+ //handle ui thread exceptions
+ Report(e.Exception);
+ //prevent application exist, so the user can copy prompted error info
+ e.Handled = true;
+ }
- public static string DependenciesInfo()
- {
- var info = $"\nPython Path: {Constant.PythonPath}\nNode Path: {Constant.NodePath}";
- return info;
- }
+ public static string RuntimeInfo()
+ {
+ var info =
+ $"""
+
+ Flow Launcher version: {Constant.Version}
+ OS Version: {ExceptionFormatter.GetWindowsFullVersionFromRegistry()}
+ IntPtr Length: {IntPtr.Size}
+ x64: {Environment.Is64BitOperatingSystem}
+ """;
+ return info;
+ }
+
+ public static string DependenciesInfo()
+ {
+ var info = $"""
+
+ Python Path: {Constant.PythonPath}
+ Node Path: {Constant.NodePath}
+ """;
+ return info;
}
}
diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs
index 3949d2828..21ddf276a 100644
--- a/Flow.Launcher/Helper/HotKeyMapper.cs
+++ b/Flow.Launcher/Helper/HotKeyMapper.cs
@@ -7,97 +7,96 @@ using Flow.Launcher.Core.Resource;
using System.Windows;
using Flow.Launcher.ViewModel;
-namespace Flow.Launcher.Helper
+namespace Flow.Launcher.Helper;
+
+internal static class HotKeyMapper
{
- internal static class HotKeyMapper
+ private static Settings _settings;
+ private static MainViewModel _mainViewModel;
+
+ internal static void Initialize(MainViewModel mainVM)
{
- private static Settings settings;
- private static MainViewModel mainViewModel;
+ _mainViewModel = mainVM;
+ _settings = _mainViewModel.Settings;
- internal static void Initialize(MainViewModel mainVM)
+ SetHotkey(_settings.Hotkey, OnToggleHotkey);
+ LoadCustomPluginHotkey();
+ }
+
+ internal static void OnToggleHotkey(object sender, HotkeyEventArgs args)
+ {
+ if (!_mainViewModel.ShouldIgnoreHotkeys())
+ _mainViewModel.ToggleFlowLauncher();
+ }
+
+ private static void SetHotkey(string hotkeyStr, EventHandler action)
+ {
+ var hotkey = new HotkeyModel(hotkeyStr);
+ SetHotkey(hotkey, action);
+ }
+
+ internal static void SetHotkey(HotkeyModel hotkey, EventHandler action)
+ {
+ string hotkeyStr = hotkey.ToString();
+ try
{
- mainViewModel = mainVM;
- settings = mainViewModel.Settings;
-
- SetHotkey(settings.Hotkey, OnToggleHotkey);
- LoadCustomPluginHotkey();
+ HotkeyManager.Current.AddOrReplace(hotkeyStr, hotkey.CharKey, hotkey.ModifierKeys, action);
}
-
- internal static void OnToggleHotkey(object sender, HotkeyEventArgs args)
+ catch (Exception)
{
- if (!mainViewModel.ShouldIgnoreHotkeys())
- mainViewModel.ToggleFlowLauncher();
- }
-
- private static void SetHotkey(string hotkeyStr, EventHandler action)
- {
- var hotkey = new HotkeyModel(hotkeyStr);
- SetHotkey(hotkey, action);
- }
-
- internal static void SetHotkey(HotkeyModel hotkey, EventHandler action)
- {
- string hotkeyStr = hotkey.ToString();
- try
- {
- HotkeyManager.Current.AddOrReplace(hotkeyStr, hotkey.CharKey, hotkey.ModifierKeys, action);
- }
- catch (Exception)
- {
- string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr);
- string errorMsgTitle = InternationalizationManager.Instance.GetTranslation("MessageBoxTitle");
- MessageBox.Show(errorMsg,errorMsgTitle);
- }
- }
-
- internal static void RemoveHotkey(string hotkeyStr)
- {
- if (!string.IsNullOrEmpty(hotkeyStr))
- {
- HotkeyManager.Current.Remove(hotkeyStr);
- }
- }
-
- internal static void LoadCustomPluginHotkey()
- {
- if (settings.CustomPluginHotkeys == null)
- return;
-
- foreach (CustomPluginHotkey hotkey in settings.CustomPluginHotkeys)
- {
- SetCustomQueryHotkey(hotkey);
- }
- }
-
- internal static void SetCustomQueryHotkey(CustomPluginHotkey hotkey)
- {
- SetHotkey(hotkey.Hotkey, (s, e) =>
- {
- if (mainViewModel.ShouldIgnoreHotkeys())
- return;
-
- mainViewModel.Show();
- mainViewModel.ChangeQueryText(hotkey.ActionKeyword, true);
- });
- }
-
- internal static bool CheckAvailability(HotkeyModel currentHotkey)
- {
- try
- {
- HotkeyManager.Current.AddOrReplace("HotkeyAvailabilityTest", currentHotkey.CharKey, currentHotkey.ModifierKeys, (sender, e) => { });
-
- return true;
- }
- catch
- {
- }
- finally
- {
- HotkeyManager.Current.Remove("HotkeyAvailabilityTest");
- }
-
- return false;
+ string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr);
+ string errorMsgTitle = InternationalizationManager.Instance.GetTranslation("MessageBoxTitle");
+ MessageBox.Show(errorMsg,errorMsgTitle);
}
}
+
+ internal static void RemoveHotkey(string hotkeyStr)
+ {
+ if (!string.IsNullOrEmpty(hotkeyStr))
+ {
+ HotkeyManager.Current.Remove(hotkeyStr);
+ }
+ }
+
+ internal static void LoadCustomPluginHotkey()
+ {
+ if (_settings.CustomPluginHotkeys == null)
+ return;
+
+ foreach (CustomPluginHotkey hotkey in _settings.CustomPluginHotkeys)
+ {
+ SetCustomQueryHotkey(hotkey);
+ }
+ }
+
+ internal static void SetCustomQueryHotkey(CustomPluginHotkey hotkey)
+ {
+ SetHotkey(hotkey.Hotkey, (s, e) =>
+ {
+ if (_mainViewModel.ShouldIgnoreHotkeys())
+ return;
+
+ _mainViewModel.Show();
+ _mainViewModel.ChangeQueryText(hotkey.ActionKeyword, true);
+ });
+ }
+
+ internal static bool CheckAvailability(HotkeyModel currentHotkey)
+ {
+ try
+ {
+ HotkeyManager.Current.AddOrReplace("HotkeyAvailabilityTest", currentHotkey.CharKey, currentHotkey.ModifierKeys, (sender, e) => { });
+
+ return true;
+ }
+ catch
+ {
+ }
+ finally
+ {
+ HotkeyManager.Current.Remove("HotkeyAvailabilityTest");
+ }
+
+ return false;
+ }
}
diff --git a/Flow.Launcher/Helper/SingletonWindowOpener.cs b/Flow.Launcher/Helper/SingletonWindowOpener.cs
index 8efc9be99..b5c2d8b55 100644
--- a/Flow.Launcher/Helper/SingletonWindowOpener.cs
+++ b/Flow.Launcher/Helper/SingletonWindowOpener.cs
@@ -2,27 +2,26 @@
using System.Linq;
using System.Windows;
-namespace Flow.Launcher.Helper
-{
- public static class SingletonWindowOpener
- {
- public static T Open(params object[] args) where T : Window
- {
- var window = Application.Current.Windows.OfType().FirstOrDefault(x => x.GetType() == typeof(T))
- ?? (T)Activator.CreateInstance(typeof(T), args);
-
- // Fix UI bug
- // Add `window.WindowState = WindowState.Normal`
- // If only use `window.Show()`, Settings-window doesn't show when minimized in taskbar
- // Not sure why this works tho
- // Probably because, when `.Show()` fails, `window.WindowState == Minimized` (not `Normal`)
- // https://stackoverflow.com/a/59719760/4230390
- window.WindowState = WindowState.Normal;
- window.Show();
-
- window.Focus();
+namespace Flow.Launcher.Helper;
- return (T)window;
- }
+public static class SingletonWindowOpener
+{
+ public static T Open(params object[] args) where T : Window
+ {
+ var window = Application.Current.Windows.OfType().FirstOrDefault(x => x.GetType() == typeof(T))
+ ?? (T)Activator.CreateInstance(typeof(T), args);
+
+ // Fix UI bug
+ // Add `window.WindowState = WindowState.Normal`
+ // If only use `window.Show()`, Settings-window doesn't show when minimized in taskbar
+ // Not sure why this works tho
+ // Probably because, when `.Show()` fails, `window.WindowState == Minimized` (not `Normal`)
+ // https://stackoverflow.com/a/59719760/4230390
+ window.WindowState = WindowState.Normal;
+ window.Show();
+
+ window.Focus();
+
+ return (T)window;
}
-}
\ No newline at end of file
+}
diff --git a/Flow.Launcher/Helper/SyntaxSugars.cs b/Flow.Launcher/Helper/SyntaxSugars.cs
index 7e0349422..41250dadf 100644
--- a/Flow.Launcher/Helper/SyntaxSugars.cs
+++ b/Flow.Launcher/Helper/SyntaxSugars.cs
@@ -1,24 +1,23 @@
using System;
-namespace Flow.Launcher.Helper
-{
- public static class SyntaxSugars
- {
- public static TResult CallOrRescueDefault(Func callback)
- {
- return CallOrRescueDefault(callback, default(TResult));
- }
+namespace Flow.Launcher.Helper;
- public static TResult CallOrRescueDefault(Func callback, TResult def)
+public static class SyntaxSugars
+{
+ public static TResult CallOrRescueDefault(Func callback)
+ {
+ return CallOrRescueDefault(callback, default(TResult));
+ }
+
+ public static TResult CallOrRescueDefault(Func callback, TResult def)
+ {
+ try
{
- try
- {
- return callback();
- }
- catch
- {
- return def;
- }
+ return callback();
+ }
+ catch
+ {
+ return def;
}
}
}
diff --git a/Flow.Launcher/Helper/WallpaperPathRetrieval.cs b/Flow.Launcher/Helper/WallpaperPathRetrieval.cs
index 9e5d77283..e08e227cc 100644
--- a/Flow.Launcher/Helper/WallpaperPathRetrieval.cs
+++ b/Flow.Launcher/Helper/WallpaperPathRetrieval.cs
@@ -5,44 +5,43 @@ using System.Text;
using System.Windows.Media;
using Microsoft.Win32;
-namespace Flow.Launcher.Helper
+namespace Flow.Launcher.Helper;
+
+public static class WallpaperPathRetrieval
{
- public static class WallpaperPathRetrieval
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)]
+ private static extern Int32 SystemParametersInfo(UInt32 action,
+ Int32 uParam, StringBuilder vParam, UInt32 winIni);
+ private static readonly UInt32 SPI_GETDESKWALLPAPER = 0x73;
+ private static int MAX_PATH = 260;
+
+ public static string GetWallpaperPath()
{
- [DllImport("user32.dll", CharSet = CharSet.Unicode)]
- private static extern Int32 SystemParametersInfo(UInt32 action,
- Int32 uParam, StringBuilder vParam, UInt32 winIni);
- private static readonly UInt32 SPI_GETDESKWALLPAPER = 0x73;
- private static int MAX_PATH = 260;
+ var wallpaper = new StringBuilder(MAX_PATH);
+ SystemParametersInfo(SPI_GETDESKWALLPAPER, MAX_PATH, wallpaper, 0);
- public static string GetWallpaperPath()
+ var str = wallpaper.ToString();
+ if (string.IsNullOrEmpty(str))
+ return null;
+
+ return str;
+ }
+
+ public static Color GetWallpaperColor()
+ {
+ RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Colors", true);
+ var result = key?.GetValue("Background", null);
+ if (result is string strResult)
{
- var wallpaper = new StringBuilder(MAX_PATH);
- SystemParametersInfo(SPI_GETDESKWALLPAPER, MAX_PATH, wallpaper, 0);
-
- var str = wallpaper.ToString();
- if (string.IsNullOrEmpty(str))
- return null;
-
- return str;
- }
-
- public static Color GetWallpaperColor()
- {
- RegistryKey key = Registry.CurrentUser.OpenSubKey("Control Panel\\Colors", true);
- var result = key.GetValue(@"Background", null);
- if (result != null && result is string)
+ try
+ {
+ var parts = strResult.Trim().Split(new[] {' '}, 3).Select(byte.Parse).ToList();
+ return Color.FromRgb(parts[0], parts[1], parts[2]);
+ }
+ catch
{
- try
- {
- var parts = result.ToString().Trim().Split(new[] {' '}, 3).Select(byte.Parse).ToList();
- return Color.FromRgb(parts[0], parts[1], parts[2]);
- }
- catch
- {
- }
}
- return Colors.Transparent;
}
+ return Colors.Transparent;
}
}
diff --git a/Flow.Launcher/Helper/WindowsInteropHelper.cs b/Flow.Launcher/Helper/WindowsInteropHelper.cs
index 16a96cd64..89fbec967 100644
--- a/Flow.Launcher/Helper/WindowsInteropHelper.cs
+++ b/Flow.Launcher/Helper/WindowsInteropHelper.cs
@@ -8,156 +8,152 @@ using System.Windows.Interop;
using System.Windows.Media;
using Point = System.Windows.Point;
-namespace Flow.Launcher.Helper
+namespace Flow.Launcher.Helper;
+
+public class WindowsInteropHelper
{
- public class WindowsInteropHelper
+ private const int GWL_STYLE = -16; //WPF's Message code for Title Bar's Style
+ private const int WS_SYSMENU = 0x80000; //WPF's Message code for System Menu
+ private static IntPtr _hwnd_shell;
+ private static IntPtr _hwnd_desktop;
+
+ //Accessors for shell and desktop handlers
+ //Will set the variables once and then will return them
+ private static IntPtr HWND_SHELL
{
- private const int GWL_STYLE = -16; //WPF's Message code for Title Bar's Style
- private const int WS_SYSMENU = 0x80000; //WPF's Message code for System Menu
- private static IntPtr _hwnd_shell;
- private static IntPtr _hwnd_desktop;
-
- //Accessors for shell and desktop handlers
- //Will set the variables once and then will return them
- private static IntPtr HWND_SHELL
+ get
{
- get
- {
- return _hwnd_shell != IntPtr.Zero ? _hwnd_shell : _hwnd_shell = GetShellWindow();
- }
+ return _hwnd_shell != IntPtr.Zero ? _hwnd_shell : _hwnd_shell = GetShellWindow();
}
- private static IntPtr HWND_DESKTOP
+ }
+ private static IntPtr HWND_DESKTOP
+ {
+ get
{
- get
- {
- return _hwnd_desktop != IntPtr.Zero ? _hwnd_desktop : _hwnd_desktop = GetDesktopWindow();
- }
+ return _hwnd_desktop != IntPtr.Zero ? _hwnd_desktop : _hwnd_desktop = GetDesktopWindow();
}
+ }
- [DllImport("user32.dll", SetLastError = true)]
- internal static extern int GetWindowLong(IntPtr hWnd, int nIndex);
-
- [DllImport("user32.dll")]
- internal static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
+ [DllImport("user32.dll", SetLastError = true)]
+ internal static extern int GetWindowLong(IntPtr hWnd, int nIndex);
- [DllImport("user32.dll")]
- internal static extern IntPtr GetForegroundWindow();
+ [DllImport("user32.dll")]
+ internal static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
- [DllImport("user32.dll")]
- internal static extern IntPtr GetDesktopWindow();
+ [DllImport("user32.dll")]
+ internal static extern IntPtr GetForegroundWindow();
- [DllImport("user32.dll")]
- internal static extern IntPtr GetShellWindow();
+ [DllImport("user32.dll")]
+ internal static extern IntPtr GetDesktopWindow();
- [DllImport("user32.dll", SetLastError = true)]
- internal static extern int GetWindowRect(IntPtr hwnd, out RECT rc);
+ [DllImport("user32.dll")]
+ internal static extern IntPtr GetShellWindow();
- [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
- internal static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
+ [DllImport("user32.dll", SetLastError = true)]
+ internal static extern int GetWindowRect(IntPtr hwnd, out RECT rc);
- [DllImport("user32.DLL")]
- public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
+ [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
+ internal static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
+
+ [DllImport("user32.DLL")]
+ public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
- const string WINDOW_CLASS_CONSOLE = "ConsoleWindowClass";
- const string WINDOW_CLASS_WINTAB = "Flip3D";
- const string WINDOW_CLASS_PROGMAN = "Progman";
- const string WINDOW_CLASS_WORKERW = "WorkerW";
+ const string WINDOW_CLASS_CONSOLE = "ConsoleWindowClass";
+ const string WINDOW_CLASS_WINTAB = "Flip3D";
+ const string WINDOW_CLASS_PROGMAN = "Progman";
+ const string WINDOW_CLASS_WORKERW = "WorkerW";
- public static bool IsWindowFullscreen()
+ public static bool IsWindowFullscreen()
+ {
+ //get current active window
+ IntPtr hWnd = GetForegroundWindow();
+
+ if (hWnd.Equals(IntPtr.Zero))
{
- //get current active window
- IntPtr hWnd = GetForegroundWindow();
-
- if (!hWnd.Equals(IntPtr.Zero))
- {
- //if current active window is NOT desktop or shell
- if (!(hWnd.Equals(HWND_DESKTOP) || hWnd.Equals(HWND_SHELL)))
- {
- StringBuilder sb = new StringBuilder(256);
- GetClassName(hWnd, sb, sb.Capacity);
- string windowClass = sb.ToString();
-
- //for Win+Tab (Flip3D)
- if (windowClass == WINDOW_CLASS_WINTAB)
- {
- return false;
- }
-
- RECT appBounds;
- GetWindowRect(hWnd, out appBounds);
-
- //for console (ConsoleWindowClass), we have to check for negative dimensions
- if (windowClass == WINDOW_CLASS_CONSOLE)
- {
- return appBounds.Top < 0 && appBounds.Bottom < 0;
- }
-
- //for desktop (Progman or WorkerW, depends on the system), we have to check
- if (windowClass == WINDOW_CLASS_PROGMAN || windowClass == WINDOW_CLASS_WORKERW)
- {
- IntPtr hWndDesktop = FindWindowEx(hWnd, IntPtr.Zero, "SHELLDLL_DefView", null);
- hWndDesktop = FindWindowEx(hWndDesktop, IntPtr.Zero, "SysListView32", "FolderView");
- if (!hWndDesktop.Equals(IntPtr.Zero))
- {
- return false;
- }
- }
-
- Rectangle screenBounds = Screen.FromHandle(hWnd).Bounds;
- if ((appBounds.Bottom - appBounds.Top) == screenBounds.Height && (appBounds.Right - appBounds.Left) == screenBounds.Width)
- {
- return true;
- }
- }
- }
-
return false;
}
- ///
- /// disable windows toolbar's control box
- /// this will also disable system menu with Alt+Space hotkey
- ///
- public static void DisableControlBox(Window win)
+ //if current active window is desktop or shell, exit early
+ if (hWnd.Equals(HWND_DESKTOP) || hWnd.Equals(HWND_SHELL))
{
- var hwnd = new WindowInteropHelper(win).Handle;
- SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
+ return false;
}
- ///
- /// Transforms pixels to Device Independent Pixels used by WPF
- ///
- /// current window, required to get presentation source
- /// horizontal position in pixels
- /// vertical position in pixels
- /// point containing device independent pixels
- public static Point TransformPixelsToDIP(Visual visual, double unitX, double unitY)
+ StringBuilder sb = new StringBuilder(256);
+ GetClassName(hWnd, sb, sb.Capacity);
+ string windowClass = sb.ToString();
+
+ //for Win+Tab (Flip3D)
+ if (windowClass == WINDOW_CLASS_WINTAB)
{
- Matrix matrix;
- var source = PresentationSource.FromVisual(visual);
- if (source != null)
+ return false;
+ }
+
+ RECT appBounds;
+ GetWindowRect(hWnd, out appBounds);
+
+ //for console (ConsoleWindowClass), we have to check for negative dimensions
+ if (windowClass == WINDOW_CLASS_CONSOLE)
+ {
+ return appBounds.Top < 0 && appBounds.Bottom < 0;
+ }
+
+ //for desktop (Progman or WorkerW, depends on the system), we have to check
+ if (windowClass is WINDOW_CLASS_PROGMAN or WINDOW_CLASS_WORKERW)
+ {
+ IntPtr hWndDesktop = FindWindowEx(hWnd, IntPtr.Zero, "SHELLDLL_DefView", null);
+ hWndDesktop = FindWindowEx(hWndDesktop, IntPtr.Zero, "SysListView32", "FolderView");
+ if (!hWndDesktop.Equals(IntPtr.Zero))
{
- matrix = source.CompositionTarget.TransformFromDevice;
+ return false;
}
- else
- {
- using (var src = new HwndSource(new HwndSourceParameters()))
- {
- matrix = src.CompositionTarget.TransformFromDevice;
- }
- }
- return new Point((int)(matrix.M11 * unitX), (int)(matrix.M22 * unitY));
}
+ Rectangle screenBounds = Screen.FromHandle(hWnd).Bounds;
+ return (appBounds.Bottom - appBounds.Top) == screenBounds.Height && (appBounds.Right - appBounds.Left) == screenBounds.Width;
+ }
- [StructLayout(LayoutKind.Sequential)]
- public struct RECT
+ ///
+ /// disable windows toolbar's control box
+ /// this will also disable system menu with Alt+Space hotkey
+ ///
+ public static void DisableControlBox(Window win)
+ {
+ var hwnd = new WindowInteropHelper(win).Handle;
+ SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
+ }
+
+ ///
+ /// Transforms pixels to Device Independent Pixels used by WPF
+ ///
+ /// current window, required to get presentation source
+ /// horizontal position in pixels
+ /// vertical position in pixels
+ /// point containing device independent pixels
+ public static Point TransformPixelsToDIP(Visual visual, double unitX, double unitY)
+ {
+ Matrix matrix;
+ var source = PresentationSource.FromVisual(visual);
+ if (source is not null)
{
- public int Left;
- public int Top;
- public int Right;
- public int Bottom;
+ matrix = source.CompositionTarget.TransformFromDevice;
}
+ else
+ {
+ using var src = new HwndSource(new HwndSourceParameters());
+ matrix = src.CompositionTarget.TransformFromDevice;
+ }
+ return new Point((int)(matrix.M11 * unitX), (int)(matrix.M22 * unitY));
+ }
+
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct RECT
+ {
+ public int Left;
+ public int Top;
+ public int Right;
+ public int Bottom;
}
}
diff --git a/Flow.Launcher/HotkeyControl.xaml b/Flow.Launcher/HotkeyControl.xaml
index acf4a21ec..02c625da5 100644
--- a/Flow.Launcher/HotkeyControl.xaml
+++ b/Flow.Launcher/HotkeyControl.xaml
@@ -3,54 +3,75 @@
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:input="clr-namespace:System.Windows.Input;assembly=PresentationCore"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- Height="24"
- d:DesignHeight="300"
- d:DesignWidth="300"
mc:Ignorable="d">
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Flow.Launcher/HotkeyControl.xaml.cs b/Flow.Launcher/HotkeyControl.xaml.cs
index 3da66caeb..a42bde7c9 100644
--- a/Flow.Launcher/HotkeyControl.xaml.cs
+++ b/Flow.Launcher/HotkeyControl.xaml.cs
@@ -1,141 +1,204 @@
-using System;
+#nullable enable
+
+using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Windows;
-using System.Windows.Controls;
using System.Windows.Input;
-using System.Windows.Media;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.Helper;
using Flow.Launcher.Infrastructure.Hotkey;
-using Flow.Launcher.Plugin;
-using System.Threading;
namespace Flow.Launcher
{
- public partial class HotkeyControl : UserControl
+ public partial class HotkeyControl
{
- public HotkeyModel CurrentHotkey { get; private set; }
- public bool CurrentHotkeyAvailable { get; private set; }
+ public IHotkeySettings HotkeySettings {
+ get { return (IHotkeySettings)GetValue(HotkeySettingsProperty); }
+ set { SetValue(HotkeySettingsProperty, value); }
+ }
- public event EventHandler HotkeyChanged;
+ public static readonly DependencyProperty HotkeySettingsProperty = DependencyProperty.Register(
+ nameof(HotkeySettings),
+ typeof(IHotkeySettings),
+ typeof(HotkeyControl),
+ new PropertyMetadata()
+ );
+ public string WindowTitle {
+ get { return (string)GetValue(WindowTitleProperty); }
+ set { SetValue(WindowTitleProperty, value); }
+ }
+
+ public static readonly DependencyProperty WindowTitleProperty = DependencyProperty.Register(
+ nameof(WindowTitle),
+ typeof(string),
+ typeof(HotkeyControl),
+ new PropertyMetadata(string.Empty)
+ );
///
/// Designed for Preview Hotkey and KeyGesture.
///
- public bool ValidateKeyGesture { get; set; } = false;
+ public static readonly DependencyProperty ValidateKeyGestureProperty = DependencyProperty.Register(
+ nameof(ValidateKeyGesture),
+ typeof(bool),
+ typeof(HotkeyControl),
+ new PropertyMetadata(default(bool))
+ );
- protected virtual void OnHotkeyChanged() => HotkeyChanged?.Invoke(this, EventArgs.Empty);
-
- public HotkeyControl()
+ public bool ValidateKeyGesture
{
- InitializeComponent();
+ get { return (bool)GetValue(ValidateKeyGestureProperty); }
+ set { SetValue(ValidateKeyGestureProperty, value); }
}
- private CancellationTokenSource hotkeyUpdateSource;
+ public static readonly DependencyProperty DefaultHotkeyProperty = DependencyProperty.Register(
+ nameof(DefaultHotkey),
+ typeof(string),
+ typeof(HotkeyControl),
+ new PropertyMetadata(default(string))
+ );
- private void TbHotkey_OnPreviewKeyDown(object sender, KeyEventArgs e)
+ public string DefaultHotkey
{
- hotkeyUpdateSource?.Cancel();
- hotkeyUpdateSource?.Dispose();
- hotkeyUpdateSource = new();
- var token = hotkeyUpdateSource.Token;
- e.Handled = true;
+ get { return (string)GetValue(DefaultHotkeyProperty); }
+ set { SetValue(DefaultHotkeyProperty, value); }
+ }
- //when alt is pressed, the real key should be e.SystemKey
- Key key = e.Key == Key.System ? e.SystemKey : e.Key;
-
- SpecialKeyState specialKeyState = GlobalHotkey.CheckModifiers();
-
- var hotkeyModel = new HotkeyModel(
- specialKeyState.AltPressed,
- specialKeyState.ShiftPressed,
- specialKeyState.WinPressed,
- specialKeyState.CtrlPressed,
- key);
-
- if (hotkeyModel.Equals(CurrentHotkey))
+ private static void OnHotkeyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (d is not HotkeyControl hotkeyControl)
{
return;
}
- _ = Dispatcher.InvokeAsync(async () =>
- {
- await Task.Delay(500, token);
- if (!token.IsCancellationRequested)
- await SetHotkeyAsync(hotkeyModel);
- });
+ hotkeyControl.SetKeysToDisplay(new HotkeyModel(hotkeyControl.Hotkey));
+ hotkeyControl.CurrentHotkey = new HotkeyModel(hotkeyControl.Hotkey);
}
- public async Task SetHotkeyAsync(HotkeyModel keyModel, bool triggerValidate = true)
- {
- tbHotkey.Text = keyModel.ToString();
- tbHotkey.Select(tbHotkey.Text.Length, 0);
+ public static readonly DependencyProperty ChangeHotkeyProperty = DependencyProperty.Register(
+ nameof(ChangeHotkey),
+ typeof(ICommand),
+ typeof(HotkeyControl),
+ new PropertyMetadata(default(ICommand))
+ );
+
+ public ICommand? ChangeHotkey
+ {
+ get { return (ICommand)GetValue(ChangeHotkeyProperty); }
+ set { SetValue(ChangeHotkeyProperty, value); }
+ }
+
+
+ public static readonly DependencyProperty HotkeyProperty = DependencyProperty.Register(
+ nameof(Hotkey),
+ typeof(string),
+ typeof(HotkeyControl),
+ new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnHotkeyChanged)
+ );
+
+ public string Hotkey
+ {
+ get { return (string)GetValue(HotkeyProperty); }
+ set { SetValue(HotkeyProperty, value); }
+ }
+
+ public HotkeyControl()
+ {
+ InitializeComponent();
+
+ HotkeyList.ItemsSource = KeysToDisplay;
+ SetKeysToDisplay(CurrentHotkey);
+ }
+
+ private static bool CheckHotkeyAvailability(HotkeyModel hotkey, bool validateKeyGesture) =>
+ hotkey.Validate(validateKeyGesture) && HotKeyMapper.CheckAvailability(hotkey);
+
+ public string EmptyHotkey => InternationalizationManager.Instance.GetTranslation("none");
+
+ public ObservableCollection KeysToDisplay { get; set; } = new();
+
+ public HotkeyModel CurrentHotkey { get; private set; } = new(false, false, false, false, Key.None);
+
+
+ public void GetNewHotkey(object sender, RoutedEventArgs e)
+ {
+ OpenHotkeyDialog();
+ }
+
+ private async Task OpenHotkeyDialog()
+ {
+ if (!string.IsNullOrEmpty(Hotkey))
+ {
+ HotKeyMapper.RemoveHotkey(Hotkey);
+ }
+
+ var dialog = new HotkeyControlDialog(Hotkey, DefaultHotkey, HotkeySettings, WindowTitle);
+ await dialog.ShowAsync();
+ switch (dialog.ResultType)
+ {
+ case HotkeyControlDialog.EResultType.Cancel:
+ SetHotkey(Hotkey);
+ return;
+ case HotkeyControlDialog.EResultType.Save:
+ SetHotkey(dialog.ResultValue);
+ break;
+ case HotkeyControlDialog.EResultType.Delete:
+ Delete();
+ break;
+ }
+ }
+
+
+ private void SetHotkey(HotkeyModel keyModel, bool triggerValidate = true)
+ {
if (triggerValidate)
{
bool hotkeyAvailable = CheckHotkeyAvailability(keyModel, ValidateKeyGesture);
- CurrentHotkeyAvailable = hotkeyAvailable;
- SetMessage(hotkeyAvailable);
- OnHotkeyChanged();
- var token = hotkeyUpdateSource.Token;
- await Task.Delay(500, token);
- if (token.IsCancellationRequested)
- return;
-
- if (CurrentHotkeyAvailable)
+ if (!hotkeyAvailable)
{
- CurrentHotkey = keyModel;
- // To trigger LostFocus
- FocusManager.SetFocusedElement(FocusManager.GetFocusScope(this), null);
- Keyboard.ClearFocus();
+ return;
}
+
+ Hotkey = keyModel.ToString();
+ SetKeysToDisplay(CurrentHotkey);
+ ChangeHotkey?.Execute(keyModel);
}
else
{
- CurrentHotkey = keyModel;
+ Hotkey = keyModel.ToString();
+ ChangeHotkey?.Execute(keyModel);
}
}
-
- public Task SetHotkeyAsync(string keyStr, bool triggerValidate = true)
+
+ public void Delete()
{
- return SetHotkeyAsync(new HotkeyModel(keyStr), triggerValidate);
+ if (!string.IsNullOrEmpty(Hotkey))
+ HotKeyMapper.RemoveHotkey(Hotkey);
+ Hotkey = "";
+ SetKeysToDisplay(new HotkeyModel(false, false, false, false, Key.None));
}
- private static bool CheckHotkeyAvailability(HotkeyModel hotkey, bool validateKeyGesture) => hotkey.Validate(validateKeyGesture) && HotKeyMapper.CheckAvailability(hotkey);
-
- public new bool IsFocused => tbHotkey.IsFocused;
-
- private void tbHotkey_LostFocus(object sender, RoutedEventArgs e)
+ private void SetKeysToDisplay(HotkeyModel? hotkey)
{
- tbHotkey.Text = CurrentHotkey?.ToString() ?? "";
- tbHotkey.Select(tbHotkey.Text.Length, 0);
- }
+ KeysToDisplay.Clear();
- private void tbHotkey_GotFocus(object sender, RoutedEventArgs e)
- {
- ResetMessage();
- }
-
- private void ResetMessage()
- {
- tbMsg.Text = InternationalizationManager.Instance.GetTranslation("flowlauncherPressHotkey");
- tbMsg.SetResourceReference(TextBox.ForegroundProperty, "Color05B");
- }
-
- private void SetMessage(bool hotkeyAvailable)
- {
- if (!hotkeyAvailable)
+ if (hotkey == null || hotkey == default(HotkeyModel))
{
- tbMsg.Foreground = new SolidColorBrush(Colors.Red);
- tbMsg.Text = InternationalizationManager.Instance.GetTranslation("hotkeyUnavailable");
+ KeysToDisplay.Add(EmptyHotkey);
+ return;
}
- else
+
+ foreach (var key in hotkey.Value.EnumerateDisplayKeys()!)
{
- tbMsg.Foreground = new SolidColorBrush(Colors.Green);
- tbMsg.Text = InternationalizationManager.Instance.GetTranslation("success");
+ KeysToDisplay.Add(key);
}
- tbMsg.Visibility = Visibility.Visible;
+ }
+
+ public void SetHotkey(string? keyStr, bool triggerValidate = true)
+ {
+ SetHotkey(new HotkeyModel(keyStr), triggerValidate);
}
}
}
diff --git a/Flow.Launcher/HotkeyControlDialog.xaml b/Flow.Launcher/HotkeyControlDialog.xaml
new file mode 100644
index 000000000..9a5872f4d
--- /dev/null
+++ b/Flow.Launcher/HotkeyControlDialog.xaml
@@ -0,0 +1,169 @@
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Flow.Launcher/HotkeyControlDialog.xaml.cs b/Flow.Launcher/HotkeyControlDialog.xaml.cs
new file mode 100644
index 000000000..a7b99f670
--- /dev/null
+++ b/Flow.Launcher/HotkeyControlDialog.xaml.cs
@@ -0,0 +1,179 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Windows;
+using System.Windows.Input;
+using Flow.Launcher.Core.Resource;
+using Flow.Launcher.Helper;
+using Flow.Launcher.Infrastructure.Hotkey;
+using Flow.Launcher.Plugin;
+using ModernWpf.Controls;
+
+namespace Flow.Launcher;
+
+#nullable enable
+
+public partial class HotkeyControlDialog : ContentDialog
+{
+ private IHotkeySettings _hotkeySettings;
+ private Action? _overwriteOtherHotkey;
+ private string DefaultHotkey { get; }
+ public string WindowTitle { get; }
+ public HotkeyModel CurrentHotkey { get; private set; }
+ public ObservableCollection KeysToDisplay { get; } = new();
+
+ public enum EResultType
+ {
+ Cancel,
+ Save,
+ Delete
+ }
+
+ public EResultType ResultType { get; private set; } = EResultType.Cancel;
+ public string ResultValue { get; private set; } = string.Empty;
+ public static string EmptyHotkey => InternationalizationManager.Instance.GetTranslation("none");
+
+ public HotkeyControlDialog(string hotkey, string defaultHotkey, IHotkeySettings hotkeySettings, string windowTitle = "")
+ {
+ WindowTitle = windowTitle switch
+ {
+ "" or null => InternationalizationManager.Instance.GetTranslation("hotkeyRegTitle"),
+ _ => windowTitle
+ };
+ DefaultHotkey = defaultHotkey;
+ CurrentHotkey = new HotkeyModel(hotkey);
+ _hotkeySettings = hotkeySettings;
+ SetKeysToDisplay(CurrentHotkey);
+
+ InitializeComponent();
+ }
+
+ private void Reset(object sender, RoutedEventArgs routedEventArgs)
+ {
+ SetKeysToDisplay(new HotkeyModel(DefaultHotkey));
+ }
+
+ private void Delete(object sender, RoutedEventArgs routedEventArgs)
+ {
+ KeysToDisplay.Clear();
+ KeysToDisplay.Add(EmptyHotkey);
+ }
+
+ private void Cancel(object sender, RoutedEventArgs routedEventArgs)
+ {
+ ResultType = EResultType.Cancel;
+ Hide();
+ }
+
+ private void Save(object sender, RoutedEventArgs routedEventArgs)
+ {
+ if (KeysToDisplay.Count == 1 && KeysToDisplay[0] == EmptyHotkey)
+ {
+ ResultType = EResultType.Delete;
+ Hide();
+ return;
+ }
+ ResultType = EResultType.Save;
+ ResultValue = string.Join("+", KeysToDisplay);
+ Hide();
+ }
+
+ private void OnPreviewKeyDown(object sender, KeyEventArgs e)
+ {
+ e.Handled = true;
+
+ //when alt is pressed, the real key should be e.SystemKey
+ Key key = e.Key == Key.System ? e.SystemKey : e.Key;
+
+ SpecialKeyState specialKeyState = GlobalHotkey.CheckModifiers();
+
+ var hotkeyModel = new HotkeyModel(
+ specialKeyState.AltPressed,
+ specialKeyState.ShiftPressed,
+ specialKeyState.WinPressed,
+ specialKeyState.CtrlPressed,
+ key);
+
+ CurrentHotkey = hotkeyModel;
+ SetKeysToDisplay(CurrentHotkey);
+ }
+
+ private void SetKeysToDisplay(HotkeyModel? hotkey)
+ {
+ _overwriteOtherHotkey = null;
+ KeysToDisplay.Clear();
+
+ if (hotkey == null || hotkey == default(HotkeyModel))
+ {
+ KeysToDisplay.Add(EmptyHotkey);
+ return;
+ }
+
+ foreach (var key in hotkey.Value.EnumerateDisplayKeys()!)
+ {
+ KeysToDisplay.Add(key);
+ }
+
+ if (tbMsg == null)
+ return;
+
+ if (_hotkeySettings.RegisteredHotkeys.FirstOrDefault(v => v.Hotkey == hotkey) is { } registeredHotkeyData)
+ {
+ var description = string.Format(
+ InternationalizationManager.Instance.GetTranslation(registeredHotkeyData.DescriptionResourceKey),
+ registeredHotkeyData.DescriptionFormatVariables
+ );
+ Alert.Visibility = Visibility.Visible;
+ if (registeredHotkeyData.RemoveHotkey is not null)
+ {
+ tbMsg.Text = string.Format(
+ InternationalizationManager.Instance.GetTranslation("hotkeyUnavailableEditable"),
+ description
+ );
+ SaveBtn.IsEnabled = false;
+ SaveBtn.Visibility = Visibility.Collapsed;
+ OverwriteBtn.IsEnabled = true;
+ OverwriteBtn.Visibility = Visibility.Visible;
+ _overwriteOtherHotkey = registeredHotkeyData.RemoveHotkey;
+ }
+ else
+ {
+ tbMsg.Text = string.Format(
+ InternationalizationManager.Instance.GetTranslation("hotkeyUnavailableUneditable"),
+ description
+ );
+ SaveBtn.IsEnabled = false;
+ SaveBtn.Visibility = Visibility.Visible;
+ OverwriteBtn.IsEnabled = false;
+ OverwriteBtn.Visibility = Visibility.Collapsed;
+ }
+ return;
+ }
+
+ OverwriteBtn.IsEnabled = false;
+ OverwriteBtn.Visibility = Visibility.Collapsed;
+
+ if (!CheckHotkeyAvailability(hotkey.Value, true))
+ {
+ tbMsg.Text = InternationalizationManager.Instance.GetTranslation("hotkeyUnavailable");
+ Alert.Visibility = Visibility.Visible;
+ SaveBtn.IsEnabled = false;
+ SaveBtn.Visibility = Visibility.Visible;
+ }
+ else
+ {
+ Alert.Visibility = Visibility.Collapsed;
+ SaveBtn.IsEnabled = true;
+ SaveBtn.Visibility = Visibility.Visible;
+ }
+ }
+
+ private static bool CheckHotkeyAvailability(HotkeyModel hotkey, bool validateKeyGesture) =>
+ hotkey.Validate(validateKeyGesture) && HotKeyMapper.CheckAvailability(hotkey);
+
+ private void Overwrite(object sender, RoutedEventArgs e)
+ {
+ _overwriteOtherHotkey?.Invoke();
+ Save(sender, e);
+ }
+}
diff --git a/Flow.Launcher/Languages/ar.xaml b/Flow.Launcher/Languages/ar.xaml
index fbb52f9ee..7f5c067f5 100644
--- a/Flow.Launcher/Languages/ar.xaml
+++ b/Flow.Launcher/Languages/ar.xaml
@@ -154,6 +154,8 @@
Dark
Sound Effect
Play a small sound when the search window opens
+ Sound Effect Volume
+ Adjust the volume of the sound effect
Animation
Use Animation in UI
Animation Speed
diff --git a/Flow.Launcher/Languages/cs.xaml b/Flow.Launcher/Languages/cs.xaml
index 90b24229b..c5ac941bc 100644
--- a/Flow.Launcher/Languages/cs.xaml
+++ b/Flow.Launcher/Languages/cs.xaml
@@ -154,6 +154,8 @@
Tmavý
Zvukový efekt
Přehrát krátký zvuk při otevření okna vyhledávání
+ Sound Effect Volume
+ Adjust the volume of the sound effect
Animace
Použít animaci v UI
Rychlost animace
diff --git a/Flow.Launcher/Languages/da.xaml b/Flow.Launcher/Languages/da.xaml
index 364b8a2da..830f49798 100644
--- a/Flow.Launcher/Languages/da.xaml
+++ b/Flow.Launcher/Languages/da.xaml
@@ -154,6 +154,8 @@
Dark
Sound Effect
Play a small sound when the search window opens
+ Sound Effect Volume
+ Adjust the volume of the sound effect
Animation
Use Animation in UI
Animation Speed
diff --git a/Flow.Launcher/Languages/de.xaml b/Flow.Launcher/Languages/de.xaml
index a360140d6..189ac678e 100644
--- a/Flow.Launcher/Languages/de.xaml
+++ b/Flow.Launcher/Languages/de.xaml
@@ -154,6 +154,8 @@
Dunkel
Soundeffekt
Ton abspielen, wenn das Suchfenster geöffnet wird
+ Sound Effect Volume
+ Adjust the volume of the sound effect
Animation
Animationen in der Oberfläche verwenden
Animation Speed
diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml
index d36a49538..fd24f825e 100644
--- a/Flow.Launcher/Languages/en.xaml
+++ b/Flow.Launcher/Languages/en.xaml
@@ -156,6 +156,8 @@
Dark
Sound Effect
Play a small sound when the search window opens
+ Sound Effect Volume
+ Adjust the volume of the sound effect
Animation
Use Animation in UI
Animation Speed
@@ -170,14 +172,35 @@
Hotkey
Hotkeys
- Flow Launcher Hotkey
+ Open Flow Launcher
Enter shortcut to show/hide Flow Launcher.
- Preview Hotkey
+ Toggle Preview
Enter shortcut to show/hide preview in search window.
+ Hotkey Presets
+ List of currently registered hotkeys
Open Result Modifier Key
Select a modifier key to open selected result via keyboard.
Show Hotkey
Show result selection hotkey with results.
+ Auto Complete
+ Runs autocomplete for the selected items.
+ Select Next Item
+ Select Previous Item
+ Next Page
+ Previous Page
+ Open Context Menu
+ Open Setting Window
+ Copy File Path
+ Toggle Game Mode
+ Toggle History
+ Open Containing Folder
+ Run As Admin
+ Refresh Search Results
+ Reload Plugins Data
+ Quick Adjust Window Width
+ Quick Adjust Window Height
+ Use when require plugins to reload and update their existing data.
+ You can add one more hotkey for this function.
Custom Query Hotkeys
Custom Query Shortcuts
Built-in Shortcuts
@@ -188,6 +211,7 @@
Delete
Edit
Add
+ None
Please select an item
Are you sure you want to delete {0} plugin hotkey?
Are you sure you want to delete shortcut: {0} with expansion {1}?
@@ -286,6 +310,11 @@
Hotkey is unavailable, please select a new hotkey
Invalid plugin hotkey
Update
+ Binding Hotkey
+ Current hotkey is unavailable.
+ This hotkey is reserved for "{0}" and can't be used. Please choose another hotkey.
+ This hotkey is already in use by "{0}". If you press "Overwrite", it will be removed from "{0}".
+ Press the keys you want to use for this function.
Custom Query Shortcut
@@ -295,8 +324,12 @@
Shortcut already exists, please enter a new Shortcut or edit the existing one.
Shortcut and/or its expansion is empty.
-
- Hotkey Unavailable
+
+ Save
+ Overwrite
+ Cancel
+ Reset
+ Delete
Version
@@ -366,6 +399,12 @@
Open Setting Window
Reload Plugin Data
+ Select first result
+ Select last result
+ Run current query again
+ Open result
+ Open result #{0}
+
Weather
Weather in Google Result
> ping 8.8.8.8
diff --git a/Flow.Launcher/Languages/es-419.xaml b/Flow.Launcher/Languages/es-419.xaml
index 8417f7c7e..9fc54c373 100644
--- a/Flow.Launcher/Languages/es-419.xaml
+++ b/Flow.Launcher/Languages/es-419.xaml
@@ -154,6 +154,8 @@
Oscuro
Efectos de Sonido
Reproducir un sonido al abrir la ventana de búsqueda
+ Sound Effect Volume
+ Adjust the volume of the sound effect
Animación
Usar Animación en la Interfaz
Animation Speed
diff --git a/Flow.Launcher/Languages/es.xaml b/Flow.Launcher/Languages/es.xaml
index bcfef1f04..5153b79f7 100644
--- a/Flow.Launcher/Languages/es.xaml
+++ b/Flow.Launcher/Languages/es.xaml
@@ -55,7 +55,7 @@
Seleccionar la última consulta
Limpiar la última consulta
Número máximo de resultados mostrados
- También puede ajustarlo rápidamente usando CTRL+Más y CTRL+Menos.
+ También puede ajustarse rápidamente usando Ctrl+Más y Ctrl+Menos.
Ignorar atajos de teclado en modo pantalla completa
No permite activar Flow Launcher con aplicaciones a pantalla completa (Recomendado para juegos).
Administrador de archivos predeterminado
@@ -73,13 +73,13 @@
Ocultar Flow Launcher al inicio
Ocultar icono en la bandeja del sistema
Cuando el icono está oculto en la bandeja del sistema, se puede abrir el menú de configuración haciendo clic con el botón derecho en la ventana de búsqueda.
- Precisión en la búsqueda de consultas
+ Precisión de búsqueda en consultas
Cambia la puntuación mínima requerida para la coincidencia de los resultados.
Buscar con Pinyin
Permite utilizar Pinyin para la búsqueda. Pinyin es el sistema estándar de ortografía romanizado para traducir chino.
Mostrar siempre vista previa
Muestra siempre el panel de vista previa al iniciar Flow. Pulse {0} para mostrar/ocultar la vista previa.
- El efecto de sombra no está permitido mientras el tema actual tenga el efecto de desenfoque activado
+ El efecto de sombra no está permitido si el tema actual tiene activado el efecto de desenfoque
Buscar complemento
@@ -154,8 +154,10 @@
Oscuro
Efecto de sonido
Reproduce un pequeño sonido cuando se abre el cuadro de búsqueda
+ Volumen del efecto de sonido
+ Ajusta el volumen del efecto de sonido
Animación
- Usar animación en la Interfaz de Usuario
+ Usa animación en la Interfaz de Usuario
Velocidad de animación
Velocidad de animación de la interfaz de usuario
Lenta
@@ -170,7 +172,7 @@
Atajos de teclado
Atajo de teclado de Flow Launcher
Introduzca el atajo de teclado para mostrar/ocultar Flow Launcher.
- Atajo de teclado para vista previa
+ Acceso directo para vista previa
Introduzca el acceso directo para mostrar/ocultar la vista previa en la ventana de búsqueda.
Tecla modificadora para abrir resultado
Seleccione una tecla modificadora para abrir el resultado seleccionado con el teclado.
@@ -194,7 +196,7 @@
Efecto de sombra de la ventana de consultas
El efecto de sombra hace un uso sustancial de la GPU. No se recomienda para ordenadores de rendimiento limitado.
Tamaño del ancho de la ventana
- También puede ajustarlo rápidamente usando Ctrl+[y Ctrl+].
+ También puede ajustarse rápidamente usando Ctrl+[ y Ctrl+].
Usar iconos Segoe Fluent
Usa iconos Segoe Fluent para los resultados de la consulta cuando sean compatibles
Pulsar Tecla
@@ -296,7 +298,7 @@ Si añade un prefijo "@" al introducir un acceso directo, éste coinci
El acceso directo y/o su expansión están vacíos.
- Atajo de teclado no disponible
+ No disponible
Versión
diff --git a/Flow.Launcher/Languages/fr.xaml b/Flow.Launcher/Languages/fr.xaml
index c33cefbfb..82b93b1e7 100644
--- a/Flow.Launcher/Languages/fr.xaml
+++ b/Flow.Launcher/Languages/fr.xaml
@@ -154,6 +154,8 @@
Sombre
Effet Sonore
Jouer un petit son lorsque la fenêtre de recherche s'ouvre
+ Volume de l'effet sonore
+ Ajuster le volume de l'effet sonore
Animation
Utiliser l'animation dans l'interface
Vitesse d'animation
diff --git a/Flow.Launcher/Languages/it.xaml b/Flow.Launcher/Languages/it.xaml
index 2b5157962..a9a6e4fee 100644
--- a/Flow.Launcher/Languages/it.xaml
+++ b/Flow.Launcher/Languages/it.xaml
@@ -154,6 +154,8 @@
Scuro
Effetto sonoro
Riproduce un piccolo suono all'apertura della finestra di ricerca
+ Sound Effect Volume
+ Adjust the volume of the sound effect
Animazione
Usa l'animazione nell'interfaccia utente
Velocità di animazione
diff --git a/Flow.Launcher/Languages/ja.xaml b/Flow.Launcher/Languages/ja.xaml
index e846613e8..f4859c806 100644
--- a/Flow.Launcher/Languages/ja.xaml
+++ b/Flow.Launcher/Languages/ja.xaml
@@ -154,6 +154,8 @@
Dark
Sound Effect
Play a small sound when the search window opens
+ Sound Effect Volume
+ Adjust the volume of the sound effect
Animation
Use Animation in UI
Animation Speed
diff --git a/Flow.Launcher/Languages/ko.xaml b/Flow.Launcher/Languages/ko.xaml
index 612836bd5..bf76718e8 100644
--- a/Flow.Launcher/Languages/ko.xaml
+++ b/Flow.Launcher/Languages/ko.xaml
@@ -154,6 +154,8 @@
어둡게
소리 효과
검색창을 열 때 작은 소리를 재생합니다.
+ Sound Effect Volume
+ Adjust the volume of the sound effect
애니메이션
일부 UI에 애니메이션을 사용합니다.
Animation Speed
diff --git a/Flow.Launcher/Languages/nb.xaml b/Flow.Launcher/Languages/nb.xaml
index fbb52f9ee..7f5c067f5 100644
--- a/Flow.Launcher/Languages/nb.xaml
+++ b/Flow.Launcher/Languages/nb.xaml
@@ -154,6 +154,8 @@
Dark
Sound Effect
Play a small sound when the search window opens
+ Sound Effect Volume
+ Adjust the volume of the sound effect
Animation
Use Animation in UI
Animation Speed
diff --git a/Flow.Launcher/Languages/nl.xaml b/Flow.Launcher/Languages/nl.xaml
index 5b28c5712..66383a90c 100644
--- a/Flow.Launcher/Languages/nl.xaml
+++ b/Flow.Launcher/Languages/nl.xaml
@@ -154,6 +154,8 @@
Donker
Geluidseffect
Een klein geluid afspelen wanneer het zoekvenster wordt geopend
+ Sound Effect Volume
+ Adjust the volume of the sound effect
Animatie
Animatie gebruiken in UI
Animation Speed
diff --git a/Flow.Launcher/Languages/pl.xaml b/Flow.Launcher/Languages/pl.xaml
index 401e202c3..8ac419ab0 100644
--- a/Flow.Launcher/Languages/pl.xaml
+++ b/Flow.Launcher/Languages/pl.xaml
@@ -154,6 +154,8 @@
Ciemny
Efekty dźwiękowe
Odtwarzaj krótki dźwięk po otwarciu okna wyszukiwania
+ Sound Effect Volume
+ Adjust the volume of the sound effect
Animacja
Użyj animacji w interfejsie użytkownika
Szybkość animacji
diff --git a/Flow.Launcher/Languages/pt-br.xaml b/Flow.Launcher/Languages/pt-br.xaml
index 23a669536..e2ddd0524 100644
--- a/Flow.Launcher/Languages/pt-br.xaml
+++ b/Flow.Launcher/Languages/pt-br.xaml
@@ -154,6 +154,8 @@
Escuro
Efeito Sonoro
Reproduzir um pequeno som ao abrir a janela de pesquisa
+ Sound Effect Volume
+ Adjust the volume of the sound effect
Animação
Utilizar Animação na Interface
Velocidade de Animação
diff --git a/Flow.Launcher/Languages/pt-pt.xaml b/Flow.Launcher/Languages/pt-pt.xaml
index 5edd8a37d..ce04e907a 100644
--- a/Flow.Launcher/Languages/pt-pt.xaml
+++ b/Flow.Launcher/Languages/pt-pt.xaml
@@ -154,6 +154,8 @@
Escuro
Efeitos sonoros
Reproduzir um som ao abrir a janela de pesquisa
+ Volume dos efeitos sonoros
+ Ajustar volume dos efeitos sonoros
Animação
Utilizar animações na aplicação
Velocidade da animação
diff --git a/Flow.Launcher/Languages/ru.xaml b/Flow.Launcher/Languages/ru.xaml
index 88f6d060d..cf1e88c44 100644
--- a/Flow.Launcher/Languages/ru.xaml
+++ b/Flow.Launcher/Languages/ru.xaml
@@ -154,6 +154,8 @@
Тёмная
Звуковой эффект
Воспроизведение небольшого звука при открытии окна поиска
+ Sound Effect Volume
+ Adjust the volume of the sound effect
Анимация
Использование анимации в меню
Скорость анимации
diff --git a/Flow.Launcher/Languages/sk.xaml b/Flow.Launcher/Languages/sk.xaml
index 252bfb81d..974895bcb 100644
--- a/Flow.Launcher/Languages/sk.xaml
+++ b/Flow.Launcher/Languages/sk.xaml
@@ -154,6 +154,8 @@
Tmavá
Zvukový efekt
Po otvorení okna vyhľadávania prehrať krátky zvuk
+ Hlasitosť zvukového efektu
+ Upraviť hlasitosť zvukového efektu
Animácia
Animovať používateľské rozhranie
Rýchlosť animácie
diff --git a/Flow.Launcher/Languages/sr.xaml b/Flow.Launcher/Languages/sr.xaml
index 0c3c0f5a7..973ee60cc 100644
--- a/Flow.Launcher/Languages/sr.xaml
+++ b/Flow.Launcher/Languages/sr.xaml
@@ -154,6 +154,8 @@
Dark
Sound Effect
Play a small sound when the search window opens
+ Sound Effect Volume
+ Adjust the volume of the sound effect
Animation
Use Animation in UI
Animation Speed
diff --git a/Flow.Launcher/Languages/tr.xaml b/Flow.Launcher/Languages/tr.xaml
index 8d5c595a2..16564c112 100644
--- a/Flow.Launcher/Languages/tr.xaml
+++ b/Flow.Launcher/Languages/tr.xaml
@@ -154,6 +154,8 @@
Koyu
Ses Efekti
Arama penceresi açıldığında küçük bir ses oynat
+ Sound Effect Volume
+ Adjust the volume of the sound effect
Animasyon
Arayüzde Animasyon Kullan
Animation Speed
diff --git a/Flow.Launcher/Languages/uk-UA.xaml b/Flow.Launcher/Languages/uk-UA.xaml
index 2adbf5d2f..46ae5d2c0 100644
--- a/Flow.Launcher/Languages/uk-UA.xaml
+++ b/Flow.Launcher/Languages/uk-UA.xaml
@@ -154,6 +154,8 @@
Темна
Звуковий ефект
Відтворювати невеликий звук при відкритті вікна пошуку
+ Гучність звукового ефекту
+ Налаштуйте гучність звукового ефекту
Анімація
Використовувати анімацію в інтерфейсі
Швидкість анімації
diff --git a/Flow.Launcher/Languages/zh-cn.xaml b/Flow.Launcher/Languages/zh-cn.xaml
index f3d2609b1..b2d9eeaa5 100644
--- a/Flow.Launcher/Languages/zh-cn.xaml
+++ b/Flow.Launcher/Languages/zh-cn.xaml
@@ -154,6 +154,8 @@
深色
音效
启用激活音效
+ 音效音量
+ 调整音效音量
动画
启用动画
动画速度
diff --git a/Flow.Launcher/Languages/zh-tw.xaml b/Flow.Launcher/Languages/zh-tw.xaml
index 5cd0591f4..4f5db8194 100644
--- a/Flow.Launcher/Languages/zh-tw.xaml
+++ b/Flow.Launcher/Languages/zh-tw.xaml
@@ -154,6 +154,8 @@
暗色系
音效
搜尋窗口打開時播放音效
+ Sound Effect Volume
+ Adjust the volume of the sound effect
動畫
使用介面動畫
Animation Speed
diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml
index e2c74d367..2e6e973a7 100644
--- a/Flow.Launcher/MainWindow.xaml
+++ b/Flow.Launcher/MainWindow.xaml
@@ -46,47 +46,14 @@
-
-
-
-
-
-
-
-
-
+ Key="End"
+ Command="{Binding SelectLastResultCommand}"
+ Modifiers="Alt" />
-
+ Command="{Binding CopyAlternativeCommand}"
+ Modifiers="Ctrl+Shift" />
+
+
+
+
+
+
+
+
+
+
@@ -216,6 +219,7 @@
_viewModel.ToggleFlowLauncher();
@@ -327,7 +342,6 @@ namespace Flow.Launcher
contextMenu.Items.Add(settings);
contextMenu.Items.Add(exit);
- _notifyIcon.ContextMenuStrip = new ContextMenuStrip(); // it need for close the context menu. if not, context menu can't close.
_notifyIcon.MouseClick += (o, e) =>
{
switch (e.Button)
@@ -335,9 +349,15 @@ namespace Flow.Launcher
case MouseButtons.Left:
_viewModel.ToggleFlowLauncher();
break;
-
case MouseButtons.Right:
+
contextMenu.IsOpen = true;
+ // Get context menu handle and bring it to the foreground
+ if (PresentationSource.FromVisual(contextMenu) is HwndSource hwndSource)
+ {
+ _ = SetForegroundWindow(hwndSource.Handle);
+ }
+ contextMenu.Focus();
break;
}
};
diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs
index 5b149f8bf..d53b7088d 100644
--- a/Flow.Launcher/PublicAPIInstance.cs
+++ b/Flow.Launcher/PublicAPIInstance.cs
@@ -311,6 +311,8 @@ namespace Flow.Launcher
public void RegisterGlobalKeyboardCallback(Func callback) => _globalKeyboardHandlers.Add(callback);
public void RemoveGlobalKeyboardCallback(Func callback) => _globalKeyboardHandlers.Remove(callback);
+ public void ReQuery(bool reselect = true) => _mainVM.ReQuery(reselect);
+
#endregion
#region Private Methods
diff --git a/Flow.Launcher/Resources/Controls/Card.xaml b/Flow.Launcher/Resources/Controls/Card.xaml
new file mode 100644
index 000000000..24a895485
--- /dev/null
+++ b/Flow.Launcher/Resources/Controls/Card.xaml
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Flow.Launcher/Resources/Controls/Card.xaml.cs b/Flow.Launcher/Resources/Controls/Card.xaml.cs
new file mode 100644
index 000000000..06266c775
--- /dev/null
+++ b/Flow.Launcher/Resources/Controls/Card.xaml.cs
@@ -0,0 +1,63 @@
+using System.Windows;
+using UserControl = System.Windows.Controls.UserControl;
+
+namespace Flow.Launcher.Resources.Controls
+{
+ public partial class Card : UserControl
+ {
+ public enum CardType
+ {
+ Default,
+ Inside,
+ InsideFit
+ }
+
+ public Card()
+ {
+ InitializeComponent();
+ }
+ public string Title
+ {
+ get { return (string)GetValue(TitleProperty); }
+ set { SetValue(TitleProperty, value); }
+ }
+ public static readonly DependencyProperty TitleProperty =
+ DependencyProperty.Register(nameof(Title), typeof(string), typeof(Card), new PropertyMetadata(string.Empty));
+
+ public string Sub
+ {
+ get { return (string)GetValue(SubProperty); }
+ set { SetValue(SubProperty, value); }
+ }
+ public static readonly DependencyProperty SubProperty =
+ DependencyProperty.Register(nameof(Sub), typeof(string), typeof(Card), new PropertyMetadata(string.Empty));
+
+ public string Icon
+ {
+ get { return (string)GetValue(IconProperty); }
+ set { SetValue(IconProperty, value); }
+ }
+ public static readonly DependencyProperty IconProperty =
+ DependencyProperty.Register(nameof(Icon), typeof(string), typeof(Card), new PropertyMetadata(string.Empty));
+
+ ///
+ /// Gets or sets additional content for the UserControl
+ ///
+ public object AdditionalContent
+ {
+ get { return (object)GetValue(AdditionalContentProperty); }
+ set { SetValue(AdditionalContentProperty, value); }
+ }
+ public static readonly DependencyProperty AdditionalContentProperty =
+ DependencyProperty.Register(nameof(AdditionalContent), typeof(object), typeof(Card),
+ new PropertyMetadata(null));
+ public CardType Type
+ {
+ get { return (CardType)GetValue(TypeProperty); }
+ set { SetValue(TypeProperty, value); }
+ }
+ public static readonly DependencyProperty TypeProperty =
+ DependencyProperty.Register(nameof(Type), typeof(CardType), typeof(Card),
+ new PropertyMetadata(CardType.Default));
+ }
+}
diff --git a/Flow.Launcher/Resources/Controls/ExCard.xaml b/Flow.Launcher/Resources/Controls/ExCard.xaml
new file mode 100644
index 000000000..8f0eb3cb4
--- /dev/null
+++ b/Flow.Launcher/Resources/Controls/ExCard.xaml
@@ -0,0 +1,313 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Flow.Launcher/Resources/Controls/ExCard.xaml.cs b/Flow.Launcher/Resources/Controls/ExCard.xaml.cs
new file mode 100644
index 000000000..f149951f0
--- /dev/null
+++ b/Flow.Launcher/Resources/Controls/ExCard.xaml.cs
@@ -0,0 +1,57 @@
+using System.Windows;
+using System.Windows.Controls;
+
+namespace Flow.Launcher.Resources.Controls
+{
+ public partial class ExCard : UserControl
+ {
+ public ExCard()
+ {
+ InitializeComponent();
+ }
+ public string Title
+ {
+ get { return (string)GetValue(TitleProperty); }
+ set { SetValue(TitleProperty, value); }
+ }
+ public static readonly DependencyProperty TitleProperty =
+ DependencyProperty.Register(nameof(Title), typeof(string), typeof(ExCard), new PropertyMetadata(string.Empty));
+
+ public string Sub
+ {
+ get { return (string)GetValue(SubProperty); }
+ set { SetValue(SubProperty, value); }
+ }
+ public static readonly DependencyProperty SubProperty =
+ DependencyProperty.Register(nameof(Sub), typeof(string), typeof(ExCard), new PropertyMetadata(string.Empty));
+
+ public string Icon
+ {
+ get { return (string)GetValue(IconProperty); }
+ set { SetValue(IconProperty, value); }
+ }
+ public static readonly DependencyProperty IconProperty =
+ DependencyProperty.Register(nameof(Icon), typeof(string), typeof(ExCard), new PropertyMetadata(string.Empty));
+
+ ///
+ /// Gets or sets additional content for the UserControl
+ ///
+ public object AdditionalContent
+ {
+ get { return (object)GetValue(AdditionalContentProperty); }
+ set { SetValue(AdditionalContentProperty, value); }
+ }
+ public static readonly DependencyProperty AdditionalContentProperty =
+ DependencyProperty.Register(nameof(AdditionalContent), typeof(object), typeof(ExCard),
+ new PropertyMetadata(null));
+
+ public object SideContent
+ {
+ get { return (object)GetValue(SideContentProperty); }
+ set { SetValue(SideContentProperty, value); }
+ }
+ public static readonly DependencyProperty SideContentProperty =
+ DependencyProperty.Register(nameof(SideContent), typeof(object), typeof(ExCard),
+ new PropertyMetadata(null));
+ }
+}
diff --git a/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml b/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml
new file mode 100644
index 000000000..448efb9cf
--- /dev/null
+++ b/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml.cs b/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml.cs
new file mode 100644
index 000000000..9b19ffd86
--- /dev/null
+++ b/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml.cs
@@ -0,0 +1,63 @@
+using System.Collections.ObjectModel;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace Flow.Launcher.Resources.Controls
+{
+ public partial class HotkeyDisplay : UserControl
+ {
+ public enum DisplayType
+ {
+ Default,
+ Small
+ }
+
+ public HotkeyDisplay()
+ {
+ InitializeComponent();
+ //List stringList =e.NewValue.Split('+').ToList();
+ Values = new ObservableCollection();
+ KeysControl.ItemsSource = Values;
+ }
+
+ public string Keys
+ {
+ get { return (string)GetValue(KeysProperty); }
+ set { SetValue(KeysProperty, value); }
+ }
+
+ public static readonly DependencyProperty KeysProperty =
+ DependencyProperty.Register(nameof(Keys), typeof(string), typeof(HotkeyDisplay),
+ new PropertyMetadata(string.Empty, keyChanged));
+
+ public DisplayType Type
+ {
+ get { return (DisplayType)GetValue(TypeProperty); }
+ set { SetValue(TypeProperty, value); }
+ }
+
+ public static readonly DependencyProperty TypeProperty =
+ DependencyProperty.Register(nameof(Type), typeof(DisplayType), typeof(HotkeyDisplay),
+ new PropertyMetadata(DisplayType.Default));
+
+ private static void keyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ var control = d as UserControl;
+ if (null == control) return; // This should not be possible
+
+ var newValue = e.NewValue as string;
+ if (null == newValue) return;
+
+ if (d is not HotkeyDisplay hotkeyDisplay)
+ return;
+
+ hotkeyDisplay.Values.Clear();
+ foreach (var key in newValue.Split('+'))
+ {
+ hotkeyDisplay.Values.Add(key);
+ }
+ }
+
+ public ObservableCollection Values { get; set; }
+ }
+}
diff --git a/Flow.Launcher/Resources/CustomControlTemplate.xaml b/Flow.Launcher/Resources/CustomControlTemplate.xaml
index 505b7c2a3..2ce53bfdb 100644
--- a/Flow.Launcher/Resources/CustomControlTemplate.xaml
+++ b/Flow.Launcher/Resources/CustomControlTemplate.xaml
@@ -3283,5 +3283,416 @@
-
+
+
diff --git a/Flow.Launcher/Resources/Dark.xaml b/Flow.Launcher/Resources/Dark.xaml
index 548a1f3a2..5f6934f30 100644
--- a/Flow.Launcher/Resources/Dark.xaml
+++ b/Flow.Launcher/Resources/Dark.xaml
@@ -104,6 +104,16 @@
#ffffff
#272727
+
+
+
+
+
+
+
+
+
+
@@ -772,7 +782,7 @@
-
+
0.6
diff --git a/Flow.Launcher/Resources/Light.xaml b/Flow.Launcher/Resources/Light.xaml
index 517a5fd5d..6e06b99af 100644
--- a/Flow.Launcher/Resources/Light.xaml
+++ b/Flow.Launcher/Resources/Light.xaml
@@ -95,6 +95,17 @@
#1b1b1b
#f6f6f6
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Flow.Launcher/Resources/Pages/WelcomePage2.xaml b/Flow.Launcher/Resources/Pages/WelcomePage2.xaml
index 033f9fe66..6c6fcbb62 100644
--- a/Flow.Launcher/Resources/Pages/WelcomePage2.xaml
+++ b/Flow.Launcher/Resources/Pages/WelcomePage2.xaml
@@ -9,6 +9,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.modernwpf.com/2019"
Title="WelcomePage2"
+ DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d">
@@ -110,12 +111,13 @@
FontWeight="SemiBold"
Text="{DynamicResource flowlauncherHotkey}" />
+ Margin="0,8,0,0"
+ ChangeHotkey="{Binding SetTogglingHotkeyCommand}"
+ DefaultHotkey="Alt+Space"
+ Hotkey="{Binding Settings.Hotkey}"
+ HotkeySettings="{Binding Settings}"
+ ValidateKeyGesture="True"
+ WindowTitle="{DynamicResource flowlauncherHotkey}" />
diff --git a/Flow.Launcher/Resources/Pages/WelcomePage2.xaml.cs b/Flow.Launcher/Resources/Pages/WelcomePage2.xaml.cs
index e89a7dd03..7dfb85a83 100644
--- a/Flow.Launcher/Resources/Pages/WelcomePage2.xaml.cs
+++ b/Flow.Launcher/Resources/Pages/WelcomePage2.xaml.cs
@@ -5,16 +5,14 @@ using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Navigation;
+using CommunityToolkit.Mvvm.Input;
+using Flow.Launcher.ViewModel;
namespace Flow.Launcher.Resources.Pages
{
public partial class WelcomePage2
{
- private Settings Settings { get; set; }
-
- private Brush tbMsgForegroundColorOriginal;
-
- private string tbMsgTextOriginal;
+ public Settings Settings { get; set; }
protected override void OnNavigatedTo(NavigationEventArgs e)
{
@@ -22,31 +20,14 @@ namespace Flow.Launcher.Resources.Pages
Settings = settings;
else
throw new ArgumentException("Unexpected Parameter setting.");
-
+
InitializeComponent();
- tbMsgTextOriginal = HotkeyControl.tbMsg.Text;
- tbMsgForegroundColorOriginal = HotkeyControl.tbMsg.Foreground;
-
- HotkeyControl.SetHotkeyAsync(Settings.Hotkey, false);
}
- private void HotkeyControl_OnGotFocus(object sender, RoutedEventArgs args)
- {
- HotKeyMapper.RemoveHotkey(Settings.Hotkey);
- }
- private void HotkeyControl_OnLostFocus(object sender, RoutedEventArgs args)
- {
- if (HotkeyControl.CurrentHotkeyAvailable)
- {
- HotKeyMapper.SetHotkey(HotkeyControl.CurrentHotkey, HotKeyMapper.OnToggleHotkey);
- Settings.Hotkey = HotkeyControl.CurrentHotkey.ToString();
- }
- else
- {
- HotKeyMapper.SetHotkey(new HotkeyModel(Settings.Hotkey), HotKeyMapper.OnToggleHotkey);
- }
- HotkeyControl.tbMsg.Text = tbMsgTextOriginal;
- HotkeyControl.tbMsg.Foreground = tbMsgForegroundColorOriginal;
+ [RelayCommand]
+ private static void SetTogglingHotkey(HotkeyModel hotkey)
+ {
+ HotKeyMapper.SetHotkey(hotkey, HotKeyMapper.OnToggleHotkey);
}
}
}
diff --git a/Flow.Launcher/Resources/Pages/WelcomePage3.xaml b/Flow.Launcher/Resources/Pages/WelcomePage3.xaml
index e7920d34e..a9e3fa696 100644
--- a/Flow.Launcher/Resources/Pages/WelcomePage3.xaml
+++ b/Flow.Launcher/Resources/Pages/WelcomePage3.xaml
@@ -2,11 +2,13 @@
x:Class="Flow.Launcher.Resources.Pages.WelcomePage3"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:cc="clr-namespace:Flow.Launcher.Resources.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Flow.Launcher.Resources.Pages"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.modernwpf.com/2019"
Title="WelcomePage3"
+ VerticalAlignment="Stretch"
mc:Ignorable="d">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
- ←
-
- ,
-
- →
-
-
-
-
-
+
-
-
-
+
+
-
-
- ↑
-
- ,
-
- ↓
-
-
-
-
-
+
-
-
-
+
+
-
-
- Enter
-
-
-
-
-
-
+
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
+
-
-
-
+
+
-
-
-
-
-
-
-
-
+
-
-
-
+
+
-
-
-
-
- +
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
- +
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- +
-
-
-
- +
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- +
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- +
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
+
+
+
+
diff --git a/Flow.Launcher/Resources/open.wav b/Flow.Launcher/Resources/open.wav
index 4f13724f7..0f692c75b 100644
Binary files a/Flow.Launcher/Resources/open.wav and b/Flow.Launcher/Resources/open.wav differ
diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml
index 1e886c022..119268d6b 100644
--- a/Flow.Launcher/SettingWindow.xaml
+++ b/Flow.Launcher/SettingWindow.xaml
@@ -2,6 +2,7 @@
x:Class="Flow.Launcher.SettingWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:cc="clr-namespace:Flow.Launcher.Resources.Controls"
xmlns:converters="clr-namespace:Flow.Launcher.Converters"
xmlns:core="clr-namespace:Flow.Launcher.Core.Resource;assembly=Flow.Launcher.Core"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@@ -67,6 +68,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
@@ -2581,63 +2648,39 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/Flow.Launcher/SettingWindow.xaml.cs b/Flow.Launcher/SettingWindow.xaml.cs
index 7301be130..8144c8ff8 100644
--- a/Flow.Launcher/SettingWindow.xaml.cs
+++ b/Flow.Launcher/SettingWindow.xaml.cs
@@ -17,6 +17,7 @@ using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Navigation;
+using NHotkey;
using Button = System.Windows.Controls.Button;
using Control = System.Windows.Controls.Control;
using KeyEventArgs = System.Windows.Input.KeyEventArgs;
@@ -110,41 +111,9 @@ namespace Flow.Launcher
#region Hotkey
- private void OnHotkeyControlLoaded(object sender, RoutedEventArgs e)
+ private void OnToggleHotkey(object sender, HotkeyEventArgs e)
{
- _ = HotkeyControl.SetHotkeyAsync(viewModel.Settings.Hotkey, false);
- }
-
- private void OnHotkeyControlFocused(object sender, RoutedEventArgs e)
- {
- HotKeyMapper.RemoveHotkey(settings.Hotkey);
- }
-
- private void OnHotkeyControlFocusLost(object sender, RoutedEventArgs e)
- {
- if (HotkeyControl.CurrentHotkeyAvailable)
- {
- HotKeyMapper.SetHotkey(HotkeyControl.CurrentHotkey, HotKeyMapper.OnToggleHotkey);
- HotKeyMapper.RemoveHotkey(settings.Hotkey);
- settings.Hotkey = HotkeyControl.CurrentHotkey.ToString();
- }
- else
- {
- HotKeyMapper.SetHotkey(new HotkeyModel(settings.Hotkey), HotKeyMapper.OnToggleHotkey);
- }
- }
-
- private void OnPreviewHotkeyControlLoaded(object sender, RoutedEventArgs e)
- {
- _ = PreviewHotkeyControl.SetHotkeyAsync(settings.PreviewHotkey, false);
- }
-
- private void OnPreviewHotkeyControlFocusLost(object sender, RoutedEventArgs e)
- {
- if (PreviewHotkeyControl.CurrentHotkeyAvailable)
- {
- settings.PreviewHotkey = PreviewHotkeyControl.CurrentHotkey.ToString();
- }
+ HotKeyMapper.OnToggleHotkey(sender, e);
}
private void OnDeleteCustomHotkeyClick(object sender, RoutedEventArgs e)
@@ -391,7 +360,7 @@ namespace Flow.Launcher
{
if (viewModel.EditSelectedCustomShortcut())
{
- customShortcutView.Items.Refresh();
+ //customShortcutView.Items.Refresh(); Should Fix
}
}
diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs
index aa300ea31..67efc6a86 100644
--- a/Flow.Launcher/ViewModel/MainViewModel.cs
+++ b/Flow.Launcher/ViewModel/MainViewModel.cs
@@ -80,6 +80,36 @@ namespace Flow.Launcher.ViewModel
case nameof(Settings.PreviewHotkey):
OnPropertyChanged(nameof(PreviewHotkey));
break;
+ case nameof(Settings.AutoCompleteHotkey):
+ OnPropertyChanged(nameof(AutoCompleteHotkey));
+ break;
+ case nameof(Settings.AutoCompleteHotkey2):
+ OnPropertyChanged(nameof(AutoCompleteHotkey2));
+ break;
+ case nameof(Settings.SelectNextItemHotkey):
+ OnPropertyChanged(nameof(SelectNextItemHotkey));
+ break;
+ case nameof(Settings.SelectNextItemHotkey2):
+ OnPropertyChanged(nameof(SelectNextItemHotkey2));
+ break;
+ case nameof(Settings.SelectPrevItemHotkey):
+ OnPropertyChanged(nameof(SelectPrevItemHotkey));
+ break;
+ case nameof(Settings.SelectPrevItemHotkey2):
+ OnPropertyChanged(nameof(SelectPrevItemHotkey2));
+ break;
+ case nameof(Settings.SelectNextPageHotkey):
+ OnPropertyChanged(nameof(SelectNextPageHotkey));
+ break;
+ case nameof(Settings.SelectPrevPageHotkey):
+ OnPropertyChanged(nameof(SelectPrevPageHotkey));
+ break;
+ case nameof(Settings.OpenContextMenuHotkey):
+ OnPropertyChanged(nameof(OpenContextMenuHotkey));
+ break;
+ case nameof(Settings.SettingWindowHotkey):
+ OnPropertyChanged(nameof(SettingWindowHotkey));
+ break;
}
};
@@ -210,7 +240,7 @@ namespace Flow.Launcher.ViewModel
}
[RelayCommand]
- private void ReQuery()
+ public void ReQuery()
{
if (SelectedIsFromQueryResults())
{
@@ -218,6 +248,14 @@ namespace Flow.Launcher.ViewModel
}
}
+ public void ReQuery(bool reselect)
+ {
+ if (SelectedIsFromQueryResults())
+ {
+ QueryResults(isReQuery: true, reSelect: reselect);
+ }
+ }
+
[RelayCommand]
private void LoadContextMenu()
{
@@ -334,6 +372,13 @@ namespace Flow.Launcher.ViewModel
SelectedResults.SelectFirstResult();
}
+ [RelayCommand]
+ private void SelectLastResult()
+ {
+ SelectedResults.SelectLastResult();
+ }
+
+
[RelayCommand]
private void SelectPrevPage()
{
@@ -619,25 +664,33 @@ namespace Flow.Launcher.ViewModel
public string OpenResultCommandModifiers => Settings.OpenResultModifiers;
- public string PreviewHotkey
+ public string VerifyOrSetDefaultHotkey(string hotkey, string defaultHotkey)
{
- get
+ try
{
- // TODO try to patch issue #1755
- // Added in v1.14.0, remove after v1.16.0.
- try
- {
- var converter = new KeyGestureConverter();
- var key = (KeyGesture)converter.ConvertFromString(Settings.PreviewHotkey);
- }
- catch (Exception e) when (e is NotSupportedException || e is InvalidEnumArgumentException)
- {
- Settings.PreviewHotkey = "F1";
- }
-
- return Settings.PreviewHotkey;
+ var converter = new KeyGestureConverter();
+ var key = (KeyGesture)converter.ConvertFromString(hotkey);
}
+ catch (Exception e) when (e is NotSupportedException || e is InvalidEnumArgumentException)
+ {
+ return defaultHotkey;
+ }
+
+ return hotkey;
}
+
+ public string PreviewHotkey => VerifyOrSetDefaultHotkey(Settings.PreviewHotkey, "F1");
+ public string AutoCompleteHotkey => VerifyOrSetDefaultHotkey(Settings.AutoCompleteHotkey, "Ctrl+Tab");
+ public string AutoCompleteHotkey2 => VerifyOrSetDefaultHotkey(Settings.AutoCompleteHotkey2, "");
+ public string SelectNextItemHotkey => VerifyOrSetDefaultHotkey(Settings.SelectNextItemHotkey, "Tab");
+ public string SelectNextItemHotkey2 => VerifyOrSetDefaultHotkey(Settings.SelectNextItemHotkey2, "");
+ public string SelectPrevItemHotkey => VerifyOrSetDefaultHotkey(Settings.SelectPrevItemHotkey, "Shift+Tab");
+ public string SelectPrevItemHotkey2 => VerifyOrSetDefaultHotkey(Settings.SelectPrevItemHotkey2, "");
+ public string SelectNextPageHotkey => VerifyOrSetDefaultHotkey(Settings.SelectNextPageHotkey, "");
+ public string SelectPrevPageHotkey => VerifyOrSetDefaultHotkey(Settings.SelectPrevPageHotkey, "");
+ public string OpenContextMenuHotkey => VerifyOrSetDefaultHotkey(Settings.OpenContextMenuHotkey, "Ctrl+O");
+ public string SettingWindowHotkey => VerifyOrSetDefaultHotkey(Settings.SettingWindowHotkey, "Ctrl+I");
+
public string Image => Constant.QueryTextBoxIconImagePath;
@@ -680,20 +733,7 @@ namespace Flow.Launcher.ViewModel
List results;
if (selected == lastContextMenuResult)
{
- // Use copy to keep the original results unchanged
- results = lastContextMenuResults.ConvertAll(result => new Result
- {
- Title = result.Title,
- SubTitle = result.SubTitle,
- IcoPath = result.IcoPath,
- PluginDirectory = result.PluginDirectory,
- Action = result.Action,
- ContextData = result.ContextData,
- Glyph = result.Glyph,
- OriginQuery = result.OriginQuery,
- Score = result.Score,
- AsyncAction = result.AsyncAction,
- });
+ results = lastContextMenuResults;
}
else
{
@@ -707,7 +747,7 @@ namespace Flow.Launcher.ViewModel
if (!string.IsNullOrEmpty(query))
{
- var filtered = results.Where
+ var filtered = results.Select(x => x.Clone()).Where
(
r =>
{
@@ -775,7 +815,7 @@ namespace Flow.Launcher.ViewModel
private readonly IReadOnlyList _emptyResult = new List();
- private async void QueryResults(bool isReQuery = false)
+ private async void QueryResults(bool isReQuery = false, bool reSelect = true)
{
_updateSource?.Cancel();
@@ -850,7 +890,7 @@ namespace Flow.Launcher.ViewModel
var tasks = plugins.Select(plugin => plugin.Metadata.Disabled switch
{
- false => QueryTask(plugin),
+ false => QueryTask(plugin, reSelect),
true => Task.CompletedTask
}).ToArray();
@@ -878,7 +918,7 @@ namespace Flow.Launcher.ViewModel
}
// Local function
- async Task QueryTask(PluginPair plugin)
+ async Task QueryTask(PluginPair plugin, bool reSelect = true)
{
// Since it is wrapped within a ThreadPool Thread, the synchronous context is null
// Task.Yield will force it to run in ThreadPool
@@ -892,7 +932,7 @@ namespace Flow.Launcher.ViewModel
results ??= _emptyResult;
if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query,
- currentCancellationToken)))
+ currentCancellationToken, reSelect)))
{
Log.Error("MainViewModel", "Unable to add item to Result Update Queue");
}
@@ -1133,7 +1173,7 @@ namespace Flow.Launcher.ViewModel
///
/// To avoid deadlock, this method should not called from main thread
///
- public void UpdateResultView(IEnumerable resultsForUpdates)
+ public void UpdateResultView(ICollection resultsForUpdates)
{
if (!resultsForUpdates.Any())
return;
@@ -1172,7 +1212,10 @@ namespace Flow.Launcher.ViewModel
}
}
- Results.AddResults(resultsForUpdates, token);
+ // it should be the same for all results
+ bool reSelect = resultsForUpdates.First().ReSelectFirstResult;
+
+ Results.AddResults(resultsForUpdates, token, reSelect);
}
#endregion
diff --git a/Flow.Launcher/ViewModel/ResultsForUpdate.cs b/Flow.Launcher/ViewModel/ResultsForUpdate.cs
index 4cb5b1a95..bc0be0de8 100644
--- a/Flow.Launcher/ViewModel/ResultsForUpdate.cs
+++ b/Flow.Launcher/ViewModel/ResultsForUpdate.cs
@@ -4,23 +4,13 @@ using System.Threading;
namespace Flow.Launcher.ViewModel
{
- public struct ResultsForUpdate
+ public record struct ResultsForUpdate(
+ IReadOnlyList Results,
+ PluginMetadata Metadata,
+ Query Query,
+ CancellationToken Token,
+ bool ReSelectFirstResult = true)
{
- public IReadOnlyList Results { get; }
-
- public PluginMetadata Metadata { get; }
- public string ID { get; }
-
- public Query Query { get; }
- public CancellationToken Token { get; }
-
- public ResultsForUpdate(IReadOnlyList results, PluginMetadata metadata, Query query, CancellationToken token)
- {
- Results = results;
- Metadata = metadata;
- Query = query;
- Token = token;
- ID = metadata.ID;
- }
+ public string ID { get; } = Metadata.ID;
}
}
diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs
index bb07ce085..db85b1ad2 100644
--- a/Flow.Launcher/ViewModel/ResultsViewModel.cs
+++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs
@@ -117,6 +117,11 @@ namespace Flow.Launcher.ViewModel
SelectedIndex = NewIndex(0);
}
+ public void SelectLastResult()
+ {
+ SelectedIndex = NewIndex(Results.Count - 1);
+ }
+
public void Clear()
{
lock (_collectionLock)
@@ -147,23 +152,23 @@ namespace Flow.Launcher.ViewModel
///
/// To avoid deadlock, this method should not called from main thread
///
- public void AddResults(IEnumerable resultsForUpdates, CancellationToken token)
+ public void AddResults(IEnumerable resultsForUpdates, CancellationToken token, bool reselect = true)
{
var newResults = NewResults(resultsForUpdates);
if (token.IsCancellationRequested)
return;
- UpdateResults(newResults, token);
+ UpdateResults(newResults, token, reselect);
}
- private void UpdateResults(List newResults, CancellationToken token = default)
+ private void UpdateResults(List newResults, CancellationToken token = default, bool reselect = true)
{
lock (_collectionLock)
{
// update UI in one run, so it can avoid UI flickering
Results.Update(newResults, token);
- if (Results.Any())
+ if (reselect && Results.Any())
SelectedItem = Results[0];
}
diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs
index 231e65539..fe1ea4e7b 100644
--- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs
+++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs
@@ -22,6 +22,8 @@ using Flow.Launcher.Plugin.SharedModels;
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.Input;
using System.Globalization;
+using System.Runtime.CompilerServices;
+using Flow.Launcher.Infrastructure.Hotkey;
namespace Flow.Launcher.ViewModel
{
@@ -63,9 +65,17 @@ namespace Flow.Launcher.ViewModel
case nameof(Settings.PreviewHotkey):
OnPropertyChanged(nameof(AlwaysPreviewToolTip));
break;
+ case nameof(Settings.SoundVolume):
+ OnPropertyChanged(nameof(SoundEffectVolume));
+ break;
}
};
+ }
+ [RelayCommand]
+ public void SetTogglingHotkey(HotkeyModel hotkey)
+ {
+ HotKeyMapper.SetHotkey(hotkey, HotKeyMapper.OnToggleHotkey);
}
public Settings Settings { get; set; }
@@ -107,13 +117,15 @@ namespace Flow.Launcher.ViewModel
}
catch (Exception e)
{
- Notification.Show(InternationalizationManager.Instance.GetTranslation("setAutoStartFailed"), e.Message);
+ Notification.Show(InternationalizationManager.Instance.GetTranslation("setAutoStartFailed"),
+ e.Message);
}
}
}
// This is only required to set at startup. When portable mode enabled/disabled a restart is always required
private bool _portableMode = DataLocation.PortableDataLocationInUse();
+
public bool PortableMode
{
get => _portableMode;
@@ -182,6 +194,7 @@ namespace Flow.Launcher.ViewModel
}
private List _lastQueryModes = new List();
+
public List LastQueryModes
{
get
@@ -190,6 +203,7 @@ namespace Flow.Launcher.ViewModel
{
_lastQueryModes = InitLastQueryModes();
}
+
return _lastQueryModes;
}
}
@@ -197,17 +211,16 @@ namespace Flow.Launcher.ViewModel
private List InitLastQueryModes()
{
var modes = new List();
- var enums = (Infrastructure.UserSettings.LastQueryMode[])Enum.GetValues(typeof(Infrastructure.UserSettings.LastQueryMode));
+ var enums = (Infrastructure.UserSettings.LastQueryMode[])Enum.GetValues(
+ typeof(Infrastructure.UserSettings.LastQueryMode));
foreach (var e in enums)
{
var key = $"LastQuery{e}";
var display = _translater.GetTranslation(key);
- var m = new LastQueryMode
- {
- Display = display, Value = e,
- };
+ var m = new LastQueryMode { Display = display, Value = e, };
modes.Add(m);
}
+
return modes;
}
@@ -264,15 +277,15 @@ namespace Flow.Launcher.ViewModel
public List OpenResultModifiersList => new List
{
- KeyConstant.Alt,
- KeyConstant.Ctrl,
- $"{KeyConstant.Ctrl}+{KeyConstant.Alt}"
+ KeyConstant.Alt, KeyConstant.Ctrl, $"{KeyConstant.Ctrl}+{KeyConstant.Alt}"
};
+
private Internationalization _translater => InternationalizationManager.Instance;
public List Languages => _translater.LoadAvailableLanguages();
public IEnumerable MaxResultsRange => Enumerable.Range(2, 16);
- public string AlwaysPreviewToolTip => string.Format(_translater.GetTranslation("AlwaysPreviewToolTip"), Settings.PreviewHotkey);
+ public string AlwaysPreviewToolTip =>
+ string.Format(_translater.GetTranslation("AlwaysPreviewToolTip"), Settings.PreviewHotkey);
public string TestProxy()
{
@@ -282,6 +295,7 @@ namespace Flow.Launcher.ViewModel
{
return InternationalizationManager.Instance.GetTranslation("serverCantBeEmpty");
}
+
if (Settings.Proxy.Port <= 0)
{
return InternationalizationManager.Instance.GetTranslation("portCantBeEmpty");
@@ -300,6 +314,7 @@ namespace Flow.Launcher.ViewModel
Credentials = new NetworkCredential(proxyUserName, Settings.Proxy.Password)
};
}
+
try
{
var response = (HttpWebResponse)request.GetResponse();
@@ -330,10 +345,7 @@ namespace Flow.Launcher.ViewModel
get => PluginManager.AllPlugins
.OrderBy(x => x.Metadata.Disabled)
.ThenBy(y => y.Metadata.Name)
- .Select(p => new PluginViewModel
- {
- PluginPair = p
- })
+ .Select(p => new PluginViewModel { PluginPair = p })
.ToList();
}
@@ -380,8 +392,7 @@ namespace Flow.Launcher.ViewModel
await PluginsManifest.UpdateManifestAsync();
OnPropertyChanged(nameof(ExternalPlugins));
}
-
-
+
internal void DisplayPluginQuery(string queryToDisplay, PluginPair plugin, int actionKeywordPosition = 0)
{
@@ -455,12 +466,10 @@ namespace Flow.Launcher.ViewModel
{
var key = $"ColorScheme{e}";
var display = _translater.GetTranslation(key);
- var m = new ColorScheme
- {
- Display = display, Value = e,
- };
+ var m = new ColorScheme { Display = display, Value = e, };
modes.Add(m);
}
+
return modes;
}
}
@@ -481,13 +490,10 @@ namespace Flow.Launcher.ViewModel
{
var key = $"SearchWindowScreen{e}";
var display = _translater.GetTranslation(key);
- var m = new SearchWindowScreen
- {
- Display = display,
- Value = e,
- };
+ var m = new SearchWindowScreen { Display = display, Value = e, };
modes.Add(m);
}
+
return modes;
}
}
@@ -508,12 +514,10 @@ namespace Flow.Launcher.ViewModel
{
var key = $"SearchWindowAlign{e}";
var display = _translater.GetTranslation(key);
- var m = new SearchWindowAlign
- {
- Display = display, Value = e,
- };
+ var m = new SearchWindowAlign { Display = display, Value = e, };
modes.Add(m);
}
+
return modes;
}
}
@@ -528,6 +532,7 @@ namespace Flow.Launcher.ViewModel
{
screenNumbers.Add(i);
}
+
return screenNumbers;
}
}
@@ -614,13 +619,10 @@ namespace Flow.Launcher.ViewModel
{
var key = $"AnimationSpeed{e}";
var display = _translater.GetTranslation(key);
- var m = new AnimationSpeed
- {
- Display = display,
- Value = e,
- };
+ var m = new AnimationSpeed { Display = display, Value = e, };
speeds.Add(m);
}
+
return speeds;
}
}
@@ -631,6 +633,12 @@ namespace Flow.Launcher.ViewModel
set => Settings.UseSound = value;
}
+ public double SoundEffectVolume
+ {
+ get => Settings.SoundVolume;
+ set => Settings.SoundVolume = value;
+ }
+
public bool UseClock
{
get => Settings.UseClock;
@@ -681,10 +689,7 @@ namespace Flow.Launcher.ViewModel
bitmap.DecodePixelWidth = 800;
bitmap.DecodePixelHeight = 600;
bitmap.EndInit();
- var brush = new ImageBrush(bitmap)
- {
- Stretch = Stretch.UniformToFill
- };
+ var brush = new ImageBrush(bitmap) { Stretch = Stretch.UniformToFill };
return brush;
}
else
@@ -705,26 +710,36 @@ namespace Flow.Launcher.ViewModel
new Result
{
Title = InternationalizationManager.Instance.GetTranslation("SampleTitleExplorer"),
- SubTitle = InternationalizationManager.Instance.GetTranslation("SampleSubTitleExplorer"),
- IcoPath = Path.Combine(Constant.ProgramDirectory, @"Plugins\Flow.Launcher.Plugin.Explorer\Images\explorer.png")
+ SubTitle =
+ InternationalizationManager.Instance.GetTranslation("SampleSubTitleExplorer"),
+ IcoPath =
+ Path.Combine(Constant.ProgramDirectory,
+ @"Plugins\Flow.Launcher.Plugin.Explorer\Images\explorer.png")
},
new Result
{
Title = InternationalizationManager.Instance.GetTranslation("SampleTitleWebSearch"),
- SubTitle = InternationalizationManager.Instance.GetTranslation("SampleSubTitleWebSearch"),
- IcoPath = Path.Combine(Constant.ProgramDirectory, @"Plugins\Flow.Launcher.Plugin.WebSearch\Images\web_search.png")
+ SubTitle =
+ InternationalizationManager.Instance.GetTranslation("SampleSubTitleWebSearch"),
+ IcoPath =
+ Path.Combine(Constant.ProgramDirectory,
+ @"Plugins\Flow.Launcher.Plugin.WebSearch\Images\web_search.png")
},
new Result
{
Title = InternationalizationManager.Instance.GetTranslation("SampleTitleProgram"),
SubTitle = InternationalizationManager.Instance.GetTranslation("SampleSubTitleProgram"),
- IcoPath = Path.Combine(Constant.ProgramDirectory, @"Plugins\Flow.Launcher.Plugin.Program\Images\program.png")
+ IcoPath =
+ Path.Combine(Constant.ProgramDirectory,
+ @"Plugins\Flow.Launcher.Plugin.Program\Images\program.png")
},
new Result
{
Title = InternationalizationManager.Instance.GetTranslation("SampleTitleProcessKiller"),
- SubTitle = InternationalizationManager.Instance.GetTranslation("SampleSubTitleProcessKiller"),
- IcoPath = Path.Combine(Constant.ProgramDirectory, @"Plugins\Flow.Launcher.Plugin.ProcessKiller\Images\app.png")
+ SubTitle =
+ InternationalizationManager.Instance.GetTranslation("SampleSubTitleProcessKiller"),
+ IcoPath = Path.Combine(Constant.ProgramDirectory,
+ @"Plugins\Flow.Launcher.Plugin.ProcessKiller\Images\app.png")
}
};
var vm = new ResultsViewModel(Settings);
@@ -880,6 +895,7 @@ namespace Flow.Launcher.ViewModel
SelectedCustomShortcut = item;
return true;
}
+
return false;
}
@@ -908,6 +924,7 @@ namespace Flow.Launcher.ViewModel
public string Documentation => Constant.Documentation;
public string Docs => Constant.Docs;
public string Github => Constant.GitHub;
+
public string Version
{
get
@@ -922,7 +939,9 @@ namespace Flow.Launcher.ViewModel
}
}
}
- public string ActivatedTimes => string.Format(_translater.GetTranslation("about_activate_times"), Settings.ActivateTimes);
+
+ public string ActivatedTimes =>
+ string.Format(_translater.GetTranslation("about_activate_times"), Settings.ActivateTimes);
public string CheckLogFolder
{
@@ -930,7 +949,8 @@ namespace Flow.Launcher.ViewModel
{
var logFiles = GetLogFiles();
long size = logFiles.Sum(file => file.Length);
- return string.Format("{0} ({1})", _translater.GetTranslation("clearlogfolder"), BytesToReadableString(size));
+ return string.Format("{0} ({1})", _translater.GetTranslation("clearlogfolder"),
+ BytesToReadableString(size));
}
}
@@ -967,10 +987,7 @@ namespace Flow.Launcher.ViewModel
internal static string BytesToReadableString(long bytes)
{
const int scale = 1024;
- string[] orders = new string[]
- {
- "GB", "MB", "KB", "B"
- };
+ string[] orders = new string[] { "GB", "MB", "KB", "B" };
long max = (long)Math.Pow(scale, orders.Length - 1);
foreach (string order in orders)
@@ -980,6 +997,7 @@ namespace Flow.Launcher.ViewModel
max /= scale;
}
+
return "0 B";
}
diff --git a/Flow.Launcher/WelcomeWindow.xaml b/Flow.Launcher/WelcomeWindow.xaml
index c7820d436..003dac5bc 100644
--- a/Flow.Launcher/WelcomeWindow.xaml
+++ b/Flow.Launcher/WelcomeWindow.xaml
@@ -10,6 +10,10 @@
Title="{DynamicResource Welcome_Page1_Title}"
Width="550"
Height="650"
+ MinWidth="550"
+ MinHeight="650"
+ MaxWidth="550"
+ MaxHeight="650"
Activated="OnActivated"
Background="{DynamicResource Color00B}"
Foreground="{DynamicResource PopupTextColor}"
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromeBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromeBookmarkLoader.cs
index 09755fe0c..65757b802 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromeBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromeBookmarkLoader.cs
@@ -3,23 +3,22 @@ using System;
using System.Collections.Generic;
using System.IO;
-namespace Flow.Launcher.Plugin.BrowserBookmark
-{
- public class ChromeBookmarkLoader : ChromiumBookmarkLoader
- {
- public override List GetBookmarks()
- {
- return LoadChromeBookmarks();
- }
+namespace Flow.Launcher.Plugin.BrowserBookmark;
- private List LoadChromeBookmarks()
- {
- var bookmarks = new List();
- var platformPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
- bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Google\Chrome\User Data"), "Google Chrome"));
- bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Google\Chrome SxS\User Data"), "Google Chrome Canary"));
- bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Chromium\User Data"), "Chromium"));
- return bookmarks;
- }
+public class ChromeBookmarkLoader : ChromiumBookmarkLoader
+{
+ public override List GetBookmarks()
+ {
+ return LoadChromeBookmarks();
}
-}
\ No newline at end of file
+
+ private List LoadChromeBookmarks()
+ {
+ var bookmarks = new List();
+ var platformPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Google\Chrome\User Data"), "Google Chrome"));
+ bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Google\Chrome SxS\User Data"), "Google Chrome Canary"));
+ bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Chromium\User Data"), "Chromium"));
+ return bookmarks;
+ }
+}
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs
index 8ce597b30..48acf6109 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs
@@ -4,91 +4,90 @@ using System.IO;
using System.Text.Json;
using Flow.Launcher.Infrastructure.Logger;
-namespace Flow.Launcher.Plugin.BrowserBookmark
+namespace Flow.Launcher.Plugin.BrowserBookmark;
+
+public abstract class ChromiumBookmarkLoader : IBookmarkLoader
{
- public abstract class ChromiumBookmarkLoader : IBookmarkLoader
+ public abstract List GetBookmarks();
+
+ protected List LoadBookmarks(string browserDataPath, string name)
{
- public abstract List GetBookmarks();
+ var bookmarks = new List();
+ if (!Directory.Exists(browserDataPath)) return bookmarks;
+ var paths = Directory.GetDirectories(browserDataPath);
- protected List LoadBookmarks(string browserDataPath, string name)
+ foreach (var profile in paths)
{
- var bookmarks = new List();
- if (!Directory.Exists(browserDataPath)) return bookmarks;
- var paths = Directory.GetDirectories(browserDataPath);
+ var bookmarkPath = Path.Combine(profile, "Bookmarks");
+ if (!File.Exists(bookmarkPath))
+ continue;
- foreach (var profile in paths)
- {
- var bookmarkPath = Path.Combine(profile, "Bookmarks");
- if (!File.Exists(bookmarkPath))
- continue;
+ Main.RegisterBookmarkFile(bookmarkPath);
- Main.RegisterBookmarkFile(bookmarkPath);
+ var source = name + (Path.GetFileName(profile) == "Default" ? "" : $" ({Path.GetFileName(profile)})");
+ bookmarks.AddRange(LoadBookmarksFromFile(bookmarkPath, source));
+ }
- var source = name + (Path.GetFileName(profile) == "Default" ? "" : $" ({Path.GetFileName(profile)})");
- bookmarks.AddRange(LoadBookmarksFromFile(bookmarkPath, source));
- }
+ return bookmarks;
+ }
+ protected List LoadBookmarksFromFile(string path, string source)
+ {
+ var bookmarks = new List();
+
+ if (!File.Exists(path))
return bookmarks;
- }
- protected List LoadBookmarksFromFile(string path, string source)
- {
- var bookmarks = new List();
-
- if (!File.Exists(path))
- return bookmarks;
-
- using var jsonDocument = JsonDocument.Parse(File.ReadAllText(path));
- if (!jsonDocument.RootElement.TryGetProperty("roots", out var rootElement))
- return bookmarks;
- EnumerateRoot(rootElement, bookmarks, source);
+ using var jsonDocument = JsonDocument.Parse(File.ReadAllText(path));
+ if (!jsonDocument.RootElement.TryGetProperty("roots", out var rootElement))
return bookmarks;
- }
+ EnumerateRoot(rootElement, bookmarks, source);
+ return bookmarks;
+ }
- private void EnumerateRoot(JsonElement rootElement, ICollection bookmarks, string source)
+ private void EnumerateRoot(JsonElement rootElement, ICollection bookmarks, string source)
+ {
+ foreach (var folder in rootElement.EnumerateObject())
{
- foreach (var folder in rootElement.EnumerateObject())
- {
- if (folder.Value.ValueKind != JsonValueKind.Object)
- continue;
+ if (folder.Value.ValueKind != JsonValueKind.Object)
+ continue;
- // Fix for Opera. It stores bookmarks slightly different than chrome. See PR and bug report for this change for details.
- // If various exceptions start to build up here consider splitting this Loader into multiple separate ones.
- if (folder.Name == "custom_root")
- EnumerateRoot(folder.Value, bookmarks, source);
- else
- EnumerateFolderBookmark(folder.Value, bookmarks, source);
+ // Fix for Opera. It stores bookmarks slightly different than chrome. See PR and bug report for this change for details.
+ // If various exceptions start to build up here consider splitting this Loader into multiple separate ones.
+ if (folder.Name == "custom_root")
+ EnumerateRoot(folder.Value, bookmarks, source);
+ else
+ EnumerateFolderBookmark(folder.Value, bookmarks, source);
+ }
+ }
+
+ private void EnumerateFolderBookmark(JsonElement folderElement, ICollection bookmarks,
+ string source)
+ {
+ if (!folderElement.TryGetProperty("children", out var childrenElement))
+ return;
+ foreach (var subElement in childrenElement.EnumerateArray())
+ {
+ if (subElement.TryGetProperty("type", out var type))
+ {
+ switch (type.GetString())
+ {
+ case "folder":
+ case "workspace": // Edge Workspace
+ EnumerateFolderBookmark(subElement, bookmarks, source);
+ break;
+ default:
+ bookmarks.Add(new Bookmark(
+ subElement.GetProperty("name").GetString(),
+ subElement.GetProperty("url").GetString(),
+ source));
+ break;
+ }
}
- }
-
- private void EnumerateFolderBookmark(JsonElement folderElement, ICollection bookmarks,
- string source)
- {
- if (!folderElement.TryGetProperty("children", out var childrenElement))
- return;
- foreach (var subElement in childrenElement.EnumerateArray())
+ else
{
- if (subElement.TryGetProperty("type", out var type))
- {
- switch (type.GetString())
- {
- case "folder":
- case "workspace": // Edge Workspace
- EnumerateFolderBookmark(subElement, bookmarks, source);
- break;
- default:
- bookmarks.Add(new Bookmark(
- subElement.GetProperty("name").GetString(),
- subElement.GetProperty("url").GetString(),
- source));
- break;
- }
- }
- else
- {
- Log.Error(
- $"ChromiumBookmarkLoader: EnumerateFolderBookmark: type property not found for {subElement.GetString()}");
- }
+ Log.Error(
+ $"ChromiumBookmarkLoader: EnumerateFolderBookmark: type property not found for {subElement.GetString()}");
}
}
}
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Commands/BookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Commands/BookmarkLoader.cs
index d08c05b6b..3468015eb 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Commands/BookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Commands/BookmarkLoader.cs
@@ -4,56 +4,55 @@ using Flow.Launcher.Infrastructure;
using Flow.Launcher.Plugin.BrowserBookmark.Models;
using Flow.Launcher.Plugin.SharedModels;
-namespace Flow.Launcher.Plugin.BrowserBookmark.Commands
+namespace Flow.Launcher.Plugin.BrowserBookmark.Commands;
+
+internal static class BookmarkLoader
{
- internal static class BookmarkLoader
+ internal static MatchResult MatchProgram(Bookmark bookmark, string queryString)
{
- internal static MatchResult MatchProgram(Bookmark bookmark, string queryString)
- {
- var match = StringMatcher.FuzzySearch(queryString, bookmark.Name);
- if (match.IsSearchPrecisionScoreMet())
- return match;
+ var match = StringMatcher.FuzzySearch(queryString, bookmark.Name);
+ if (match.IsSearchPrecisionScoreMet())
+ return match;
- return StringMatcher.FuzzySearch(queryString, bookmark.Url);
+ return StringMatcher.FuzzySearch(queryString, bookmark.Url);
+ }
+
+ internal static List LoadAllBookmarks(Settings setting)
+ {
+ var allBookmarks = new List();
+
+ if (setting.LoadChromeBookmark)
+ {
+ // Add Chrome bookmarks
+ var chromeBookmarks = new ChromeBookmarkLoader();
+ allBookmarks.AddRange(chromeBookmarks.GetBookmarks());
}
- internal static List LoadAllBookmarks(Settings setting)
+ if (setting.LoadFirefoxBookmark)
{
- var allBookmarks = new List();
-
- if (setting.LoadChromeBookmark)
- {
- // Add Chrome bookmarks
- var chromeBookmarks = new ChromeBookmarkLoader();
- allBookmarks.AddRange(chromeBookmarks.GetBookmarks());
- }
-
- if (setting.LoadFirefoxBookmark)
- {
- // Add Firefox bookmarks
- var mozBookmarks = new FirefoxBookmarkLoader();
- allBookmarks.AddRange(mozBookmarks.GetBookmarks());
- }
-
- if (setting.LoadEdgeBookmark)
- {
- // Add Edge (Chromium) bookmarks
- var edgeBookmarks = new EdgeBookmarkLoader();
- allBookmarks.AddRange(edgeBookmarks.GetBookmarks());
- }
-
- foreach (var browser in setting.CustomChromiumBrowsers)
- {
- IBookmarkLoader loader = browser.BrowserType switch
- {
- BrowserType.Chromium => new CustomChromiumBookmarkLoader(browser),
- BrowserType.Firefox => new CustomFirefoxBookmarkLoader(browser),
- _ => new CustomChromiumBookmarkLoader(browser),
- };
- allBookmarks.AddRange(loader.GetBookmarks());
- }
-
- return allBookmarks.Distinct().ToList();
+ // Add Firefox bookmarks
+ var mozBookmarks = new FirefoxBookmarkLoader();
+ allBookmarks.AddRange(mozBookmarks.GetBookmarks());
}
+
+ if (setting.LoadEdgeBookmark)
+ {
+ // Add Edge (Chromium) bookmarks
+ var edgeBookmarks = new EdgeBookmarkLoader();
+ allBookmarks.AddRange(edgeBookmarks.GetBookmarks());
+ }
+
+ foreach (var browser in setting.CustomChromiumBrowsers)
+ {
+ IBookmarkLoader loader = browser.BrowserType switch
+ {
+ BrowserType.Chromium => new CustomChromiumBookmarkLoader(browser),
+ BrowserType.Firefox => new CustomFirefoxBookmarkLoader(browser),
+ _ => new CustomChromiumBookmarkLoader(browser),
+ };
+ allBookmarks.AddRange(loader.GetBookmarks());
+ }
+
+ return allBookmarks.Distinct().ToList();
}
}
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/CustomChromiumBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/CustomChromiumBookmarkLoader.cs
index fa98f4d7c..005c83992 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/CustomChromiumBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/CustomChromiumBookmarkLoader.cs
@@ -1,19 +1,18 @@
using Flow.Launcher.Plugin.BrowserBookmark.Models;
using System.Collections.Generic;
-namespace Flow.Launcher.Plugin.BrowserBookmark
-{
- public class CustomChromiumBookmarkLoader : ChromiumBookmarkLoader
- {
- public CustomChromiumBookmarkLoader(CustomBrowser browser)
- {
- BrowserName = browser.Name;
- BrowserDataPath = browser.DataDirectoryPath;
- }
- public string BrowserDataPath { get; init; }
- public string BookmarkFilePath { get; init; }
- public string BrowserName { get; init; }
+namespace Flow.Launcher.Plugin.BrowserBookmark;
- public override List GetBookmarks() => BrowserDataPath != null ? LoadBookmarks(BrowserDataPath, BrowserName) : LoadBookmarksFromFile(BookmarkFilePath, BrowserName);
+public class CustomChromiumBookmarkLoader : ChromiumBookmarkLoader
+{
+ public CustomChromiumBookmarkLoader(CustomBrowser browser)
+ {
+ BrowserName = browser.Name;
+ BrowserDataPath = browser.DataDirectoryPath;
}
-}
\ No newline at end of file
+ public string BrowserDataPath { get; init; }
+ public string BookmarkFilePath { get; init; }
+ public string BrowserName { get; init; }
+
+ public override List GetBookmarks() => BrowserDataPath != null ? LoadBookmarks(BrowserDataPath, BrowserName) : LoadBookmarksFromFile(BookmarkFilePath, BrowserName);
+}
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/CustomFirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/CustomFirefoxBookmarkLoader.cs
index 82bdc29f5..d0bb7b0cc 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/CustomFirefoxBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/CustomFirefoxBookmarkLoader.cs
@@ -2,26 +2,25 @@
using System.IO;
using Flow.Launcher.Plugin.BrowserBookmark.Models;
-namespace Flow.Launcher.Plugin.BrowserBookmark
-{
- public class CustomFirefoxBookmarkLoader : FirefoxBookmarkLoaderBase
- {
- public CustomFirefoxBookmarkLoader(CustomBrowser browser)
- {
- BrowserName = browser.Name;
- BrowserDataPath = browser.DataDirectoryPath;
- }
-
- ///
- /// Path to places.sqlite
- ///
- public string BrowserDataPath { get; init; }
-
- public string BrowserName { get; init; }
+namespace Flow.Launcher.Plugin.BrowserBookmark;
- public override List GetBookmarks()
- {
- return GetBookmarksFromPath(Path.Combine(BrowserDataPath, "places.sqlite"));
- }
+public class CustomFirefoxBookmarkLoader : FirefoxBookmarkLoaderBase
+{
+ public CustomFirefoxBookmarkLoader(CustomBrowser browser)
+ {
+ BrowserName = browser.Name;
+ BrowserDataPath = browser.DataDirectoryPath;
+ }
+
+ ///
+ /// Path to places.sqlite
+ ///
+ public string BrowserDataPath { get; init; }
+
+ public string BrowserName { get; init; }
+
+ public override List GetBookmarks()
+ {
+ return GetBookmarksFromPath(Path.Combine(BrowserDataPath, "places.sqlite"));
}
}
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/EdgeBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/EdgeBookmarkLoader.cs
index 79190f0ef..40123b022 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/EdgeBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/EdgeBookmarkLoader.cs
@@ -3,21 +3,20 @@ using System;
using System.Collections.Generic;
using System.IO;
-namespace Flow.Launcher.Plugin.BrowserBookmark
-{
- public class EdgeBookmarkLoader : ChromiumBookmarkLoader
- {
- private List LoadEdgeBookmarks()
- {
- var bookmarks = new List();
- var platformPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
- bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Microsoft\Edge\User Data"), "Microsoft Edge"));
- bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Microsoft\Edge Dev\User Data"), "Microsoft Edge Dev"));
- bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Microsoft\Edge SxS\User Data"), "Microsoft Edge Canary"));
+namespace Flow.Launcher.Plugin.BrowserBookmark;
- return bookmarks;
- }
-
- public override List GetBookmarks() => LoadEdgeBookmarks();
+public class EdgeBookmarkLoader : ChromiumBookmarkLoader
+{
+ private List LoadEdgeBookmarks()
+ {
+ var bookmarks = new List();
+ var platformPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Microsoft\Edge\User Data"), "Microsoft Edge"));
+ bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Microsoft\Edge Dev\User Data"), "Microsoft Edge Dev"));
+ bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Microsoft\Edge SxS\User Data"), "Microsoft Edge Canary"));
+
+ return bookmarks;
}
-}
\ No newline at end of file
+
+ public override List GetBookmarks() => LoadEdgeBookmarks();
+}
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
index 3d061e758..35ad32fb3 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
@@ -5,140 +5,133 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
-namespace Flow.Launcher.Plugin.BrowserBookmark
+namespace Flow.Launcher.Plugin.BrowserBookmark;
+
+public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
{
- public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
- {
- public abstract List GetBookmarks();
+ public abstract List GetBookmarks();
- private const string queryAllBookmarks = @"SELECT moz_places.url, moz_bookmarks.title
- FROM moz_places
- INNER JOIN moz_bookmarks ON (
+ private const string QueryAllBookmarks = """
+ SELECT moz_places.url, moz_bookmarks.title
+ FROM moz_places
+ INNER JOIN moz_bookmarks ON (
moz_bookmarks.fk NOT NULL AND moz_bookmarks.title NOT NULL AND moz_bookmarks.fk = moz_places.id
- )
- ORDER BY moz_places.visit_count DESC
- ";
+ )
+ ORDER BY moz_places.visit_count DESC
+ """;
- private const string dbPathFormat = "Data Source ={0}";
+ private const string DbPathFormat = "Data Source ={0}";
- protected static List GetBookmarksFromPath(string placesPath)
- {
- // Return empty list if the places.sqlite file cannot be found
- if (string.IsNullOrEmpty(placesPath) || !File.Exists(placesPath))
- return new List();
+ protected static List GetBookmarksFromPath(string placesPath)
+ {
+ // Return empty list if the places.sqlite file cannot be found
+ if (string.IsNullOrEmpty(placesPath) || !File.Exists(placesPath))
+ return new List();
- var bookmarkList = new List();
+ Main.RegisterBookmarkFile(placesPath);
- Main.RegisterBookmarkFile(placesPath);
+ // create the connection string and init the connection
+ string dbPath = string.Format(DbPathFormat, placesPath);
+ using var dbConnection = new SqliteConnection(dbPath);
+ // Open connection to the database file and execute the query
+ dbConnection.Open();
+ var reader = new SqliteCommand(QueryAllBookmarks, dbConnection).ExecuteReader();
- // create the connection string and init the connection
- string dbPath = string.Format(dbPathFormat, placesPath);
- using var dbConnection = new SqliteConnection(dbPath);
- // Open connection to the database file and execute the query
- dbConnection.Open();
- var reader = new SqliteCommand(queryAllBookmarks, dbConnection).ExecuteReader();
-
- // return results in List format
- bookmarkList = reader.Select(
- x => new Bookmark(x["title"] is DBNull ? string.Empty : x["title"].ToString(),
- x["url"].ToString())
- ).ToList();
+ // return results in List format
+ return reader
+ .Select(
+ x => new Bookmark(
+ x["title"] is DBNull ? string.Empty : x["title"].ToString(),
+ x["url"].ToString()
+ )
+ )
+ .ToList();
+ }
+}
- return bookmarkList;
- }
+
+public class FirefoxBookmarkLoader : FirefoxBookmarkLoaderBase
+{
+ ///
+ /// Searches the places.sqlite db and returns all bookmarks
+ ///
+ public override List GetBookmarks()
+ {
+ return GetBookmarksFromPath(PlacesPath);
}
-
- public class FirefoxBookmarkLoader : FirefoxBookmarkLoaderBase
+ ///
+ /// Path to places.sqlite
+ ///
+ private string PlacesPath
{
- ///
- /// Searches the places.sqlite db and returns all bookmarks
- ///
- public override List GetBookmarks()
+ get
{
- return GetBookmarksFromPath(PlacesPath);
- }
-
- ///
- /// Path to places.sqlite
- ///
- private string PlacesPath
- {
- get
- {
- var profileFolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"Mozilla\Firefox");
- var profileIni = Path.Combine(profileFolderPath, @"profiles.ini");
+ var profileFolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"Mozilla\Firefox");
+ var profileIni = Path.Combine(profileFolderPath, @"profiles.ini");
- if (!File.Exists(profileIni))
- return string.Empty;
+ if (!File.Exists(profileIni))
+ return string.Empty;
- // get firefox default profile directory from profiles.ini
- string ini;
- using (var sReader = new StreamReader(profileIni))
- {
- ini = sReader.ReadToEnd();
- }
+ // get firefox default profile directory from profiles.ini
+ using var sReader = new StreamReader(profileIni);
+ var ini = sReader.ReadToEnd();
- /*
- Current profiles.ini structure example as of Firefox version 69.0.1
-
- [Install736426B0AF4A39CB]
- Default=Profiles/7789f565.default-release <== this is the default profile this plugin will get the bookmarks from. When opened Firefox will load the default profile
- Locked=1
+ /*
+ Current profiles.ini structure example as of Firefox version 69.0.1
- [Profile2]
- Name=newblahprofile
- IsRelative=0
- Path=C:\t6h2yuq8.newblahprofile <== Note this is a custom location path for the profile user can set, we need to cater for this in code.
+ [Install736426B0AF4A39CB]
+ Default=Profiles/7789f565.default-release <== this is the default profile this plugin will get the bookmarks from. When opened Firefox will load the default profile
+ Locked=1
- [Profile1]
- Name=default
- IsRelative=1
- Path=Profiles/cydum7q4.default
- Default=1
+ [Profile2]
+ Name=newblahprofile
+ IsRelative=0
+ Path=C:\t6h2yuq8.newblahprofile <== Note this is a custom location path for the profile user can set, we need to cater for this in code.
- [Profile0]
- Name=default-release
- IsRelative=1
- Path=Profiles/7789f565.default-release
+ [Profile1]
+ Name=default
+ IsRelative=1
+ Path=Profiles/cydum7q4.default
+ Default=1
- [General]
- StartWithLastProfile=1
- Version=2
- */
+ [Profile0]
+ Name=default-release
+ IsRelative=1
+ Path=Profiles/7789f565.default-release
- var lines = ini.Split(new string[]
- {
- "\r\n"
- }, StringSplitOptions.None).ToList();
+ [General]
+ StartWithLastProfile=1
+ Version=2
+ */
+ var lines = ini.Split("\r\n").ToList();
- var defaultProfileFolderNameRaw = lines.Where(x => x.Contains("Default=") && x != "Default=1").FirstOrDefault() ?? string.Empty;
+ var defaultProfileFolderNameRaw = lines.FirstOrDefault(x => x.Contains("Default=") && x != "Default=1") ?? string.Empty;
- if (string.IsNullOrEmpty(defaultProfileFolderNameRaw))
- return string.Empty;
+ if (string.IsNullOrEmpty(defaultProfileFolderNameRaw))
+ return string.Empty;
- var defaultProfileFolderName = defaultProfileFolderNameRaw.Split('=').Last();
+ var defaultProfileFolderName = defaultProfileFolderNameRaw.Split('=').Last();
- var indexOfDefaultProfileAtttributePath = lines.IndexOf("Path=" + defaultProfileFolderName);
+ var indexOfDefaultProfileAttributePath = lines.IndexOf("Path=" + defaultProfileFolderName);
- // Seen in the example above, the IsRelative attribute is always above the Path attribute
- var relativeAttribute = lines[indexOfDefaultProfileAtttributePath - 1];
+ // Seen in the example above, the IsRelative attribute is always above the Path attribute
+ var relativeAttribute = lines[indexOfDefaultProfileAttributePath - 1];
- return relativeAttribute == "0" // See above, the profile is located in a custom location, path is not relative, so IsRelative=0
- ? defaultProfileFolderName + @"\places.sqlite"
- : Path.Combine(profileFolderPath, defaultProfileFolderName) + @"\places.sqlite";
- }
- }
- }
-
- public static class Extensions
- {
- public static IEnumerable Select(this SqliteDataReader reader, Func projection)
- {
- while (reader.Read())
- {
- yield return projection(reader);
- }
+ return relativeAttribute == "0" // See above, the profile is located in a custom location, path is not relative, so IsRelative=0
+ ? defaultProfileFolderName + @"\places.sqlite"
+ : Path.Combine(profileFolderPath, defaultProfileFolderName) + @"\places.sqlite";
+ }
+ }
+}
+
+public static class Extensions
+{
+ public static IEnumerable Select(this SqliteDataReader reader, Func projection)
+ {
+ while (reader.Read())
+ {
+ yield return projection(reader);
}
}
}
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj
index 81374abe8..fe118c2c3 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj
@@ -56,7 +56,7 @@
-
+
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/IBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/IBookmarkLoader.cs
index 2c48cfd55..8a9727352 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/IBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/IBookmarkLoader.cs
@@ -1,10 +1,9 @@
using Flow.Launcher.Plugin.BrowserBookmark.Models;
using System.Collections.Generic;
-namespace Flow.Launcher.Plugin.BrowserBookmark
+namespace Flow.Launcher.Plugin.BrowserBookmark;
+
+public interface IBookmarkLoader
{
- public interface IBookmarkLoader
- {
- public List GetBookmarks();
- }
-}
\ No newline at end of file
+ public List GetBookmarks();
+}
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs
index a13d6c929..a48d70f2d 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Windows;
using System.Windows.Controls;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Plugin.BrowserBookmark.Commands;
@@ -12,233 +11,234 @@ using System.Threading.Channels;
using System.Threading.Tasks;
using System.Threading;
-namespace Flow.Launcher.Plugin.BrowserBookmark
+namespace Flow.Launcher.Plugin.BrowserBookmark;
+
+public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, IContextMenu, IDisposable
{
- public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, IContextMenu, IDisposable
+ private static PluginInitContext _context;
+
+ private static List _cachedBookmarks = new List();
+
+ private static Settings _settings;
+
+ private static bool _initialized = false;
+
+ public void Init(PluginInitContext context)
{
- private static PluginInitContext context;
+ _context = context;
- private static List cachedBookmarks = new List();
+ _settings = context.API.LoadSettingJsonStorage();
- private static Settings _settings;
+ LoadBookmarksIfEnabled();
+ }
- private static bool initialized = false;
-
- public void Init(PluginInitContext context)
+ private static void LoadBookmarksIfEnabled()
+ {
+ if (_context.CurrentPluginMetadata.Disabled)
{
- Main.context = context;
+ // Don't load or monitor files if disabled
+ return;
+ }
- _settings = context.API.LoadSettingJsonStorage();
+ _cachedBookmarks = BookmarkLoader.LoadAllBookmarks(_settings);
+ _ = MonitorRefreshQueueAsync();
+ _initialized = true;
+ }
+ public List Query(Query query)
+ {
+ // For when the plugin being previously disabled and is now re-enabled
+ if (!_initialized)
+ {
LoadBookmarksIfEnabled();
}
- private static void LoadBookmarksIfEnabled()
+ string param = query.Search.TrimStart();
+
+ // Should top results be returned? (true if no search parameters have been passed)
+ var topResults = string.IsNullOrEmpty(param);
+
+
+ if (!topResults)
{
- if (context.CurrentPluginMetadata.Disabled)
- {
- // Don't load or monitor files if disabled
- return;
- }
-
- cachedBookmarks = BookmarkLoader.LoadAllBookmarks(_settings);
- _ = MonitorRefreshQueueAsync();
- initialized = true;
- }
-
- public List Query(Query query)
- {
- // For when the plugin being previously disabled and is now renabled
- if (!initialized)
- {
- LoadBookmarksIfEnabled();
- }
-
- string param = query.Search.TrimStart();
-
- // Should top results be returned? (true if no search parameters have been passed)
- var topResults = string.IsNullOrEmpty(param);
-
-
- if (!topResults)
- {
- // Since we mixed chrome and firefox bookmarks, we should order them again
- var returnList = cachedBookmarks.Select(c => new Result()
- {
- Title = c.Name,
- SubTitle = c.Url,
- IcoPath = @"Images\bookmark.png",
- Score = BookmarkLoader.MatchProgram(c, param).Score,
- Action = _ =>
+ // Since we mixed chrome and firefox bookmarks, we should order them again
+ return _cachedBookmarks
+ .Select(
+ c => new Result
{
- context.API.OpenUrl(c.Url);
-
- return true;
- },
- ContextData = new BookmarkAttributes
- {
- Url = c.Url
- }
- }).Where(r => r.Score > 0);
- return returnList.ToList();
- }
- else
- {
- return cachedBookmarks.Select(c => new Result()
- {
- Title = c.Name,
- SubTitle = c.Url,
- IcoPath = @"Images\bookmark.png",
- Score = 5,
- Action = _ =>
- {
- context.API.OpenUrl(c.Url);
- return true;
- },
- ContextData = new BookmarkAttributes
- {
- Url = c.Url
- }
- }).ToList();
- }
- }
-
-
- private static Channel refreshQueue = Channel.CreateBounded(1);
-
- private static SemaphoreSlim fileMonitorSemaphore = new(1, 1);
-
- private static async Task MonitorRefreshQueueAsync()
- {
- if (fileMonitorSemaphore.CurrentCount < 1)
- {
- return;
- }
- await fileMonitorSemaphore.WaitAsync();
- var reader = refreshQueue.Reader;
- while (await reader.WaitToReadAsync())
- {
- if (reader.TryRead(out _))
- {
- ReloadAllBookmarks(false);
- }
- }
- fileMonitorSemaphore.Release();
- }
-
- private static readonly List Watchers = new();
-
- internal static void RegisterBookmarkFile(string path)
- {
- var directory = Path.GetDirectoryName(path);
- if (!Directory.Exists(directory) || !File.Exists(path))
- {
- return;
- }
- if (Watchers.Any(x => x.Path.Equals(directory, StringComparison.OrdinalIgnoreCase)))
- {
- return;
- }
-
- var watcher = new FileSystemWatcher(directory!);
- watcher.Filter = Path.GetFileName(path);
-
- watcher.NotifyFilter = NotifyFilters.FileName |
- NotifyFilters.LastWrite |
- NotifyFilters.Size;
-
- watcher.Changed += static (_, _) =>
- {
- refreshQueue.Writer.TryWrite(default);
- };
-
- watcher.Renamed += static (_, _) =>
- {
- refreshQueue.Writer.TryWrite(default);
- };
-
- watcher.EnableRaisingEvents = true;
-
- Watchers.Add(watcher);
- }
-
- public void ReloadData()
- {
- ReloadAllBookmarks();
- }
-
- public static void ReloadAllBookmarks(bool disposeFileWatchers = true)
- {
- cachedBookmarks.Clear();
- if (disposeFileWatchers)
- DisposeFileWatchers();
- LoadBookmarksIfEnabled();
- }
-
- public string GetTranslatedPluginTitle()
- {
- return context.API.GetTranslation("flowlauncher_plugin_browserbookmark_plugin_name");
- }
-
- public string GetTranslatedPluginDescription()
- {
- return context.API.GetTranslation("flowlauncher_plugin_browserbookmark_plugin_description");
- }
-
- public Control CreateSettingPanel()
- {
- return new SettingsControl(_settings);
- }
-
- public List LoadContextMenus(Result selectedResult)
- {
- return new List()
- {
- new Result
- {
- Title = context.API.GetTranslation("flowlauncher_plugin_browserbookmark_copyurl_title"),
- SubTitle = context.API.GetTranslation("flowlauncher_plugin_browserbookmark_copyurl_subtitle"),
- Action = _ =>
- {
- try
+ Title = c.Name,
+ SubTitle = c.Url,
+ IcoPath = @"Images\bookmark.png",
+ Score = BookmarkLoader.MatchProgram(c, param).Score,
+ Action = _ =>
{
- context.API.CopyToClipboard(((BookmarkAttributes)selectedResult.ContextData).Url);
+ _context.API.OpenUrl(c.Url);
return true;
- }
- catch (Exception e)
+ },
+ ContextData = new BookmarkAttributes { Url = c.Url }
+ }
+ )
+ .Where(r => r.Score > 0)
+ .ToList();
+ }
+ else
+ {
+ return _cachedBookmarks
+ .Select(
+ c => new Result
+ {
+ Title = c.Name,
+ SubTitle = c.Url,
+ IcoPath = @"Images\bookmark.png",
+ Score = 5,
+ Action = _ =>
{
- var message = "Failed to set url in clipboard";
- Log.Exception("Main", message, e, "LoadContextMenus");
-
- context.API.ShowMsg(message);
-
- return false;
- }
- },
- IcoPath = "Images\\copylink.png",
- Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue8c8")
- }
- };
- }
-
- internal class BookmarkAttributes
- {
- internal string Url { get; set; }
- }
-
- public void Dispose()
- {
- DisposeFileWatchers();
- }
-
- private static void DisposeFileWatchers()
- {
- foreach (var watcher in Watchers)
- {
- watcher.Dispose();
- }
- Watchers.Clear();
+ _context.API.OpenUrl(c.Url);
+ return true;
+ },
+ ContextData = new BookmarkAttributes { Url = c.Url }
+ }
+ )
+ .ToList();
}
}
+
+
+ private static Channel _refreshQueue = Channel.CreateBounded(1);
+
+ private static SemaphoreSlim _fileMonitorSemaphore = new(1, 1);
+
+ private static async Task MonitorRefreshQueueAsync()
+ {
+ if (_fileMonitorSemaphore.CurrentCount < 1)
+ {
+ return;
+ }
+ await _fileMonitorSemaphore.WaitAsync();
+ var reader = _refreshQueue.Reader;
+ while (await reader.WaitToReadAsync())
+ {
+ if (reader.TryRead(out _))
+ {
+ ReloadAllBookmarks(false);
+ }
+ }
+ _fileMonitorSemaphore.Release();
+ }
+
+ private static readonly List Watchers = new();
+
+ internal static void RegisterBookmarkFile(string path)
+ {
+ var directory = Path.GetDirectoryName(path);
+ if (!Directory.Exists(directory) || !File.Exists(path))
+ {
+ return;
+ }
+ if (Watchers.Any(x => x.Path.Equals(directory, StringComparison.OrdinalIgnoreCase)))
+ {
+ return;
+ }
+
+ var watcher = new FileSystemWatcher(directory!);
+ watcher.Filter = Path.GetFileName(path);
+
+ watcher.NotifyFilter = NotifyFilters.FileName |
+ NotifyFilters.LastWrite |
+ NotifyFilters.Size;
+
+ watcher.Changed += static (_, _) =>
+ {
+ _refreshQueue.Writer.TryWrite(default);
+ };
+
+ watcher.Renamed += static (_, _) =>
+ {
+ _refreshQueue.Writer.TryWrite(default);
+ };
+
+ watcher.EnableRaisingEvents = true;
+
+ Watchers.Add(watcher);
+ }
+
+ public void ReloadData()
+ {
+ ReloadAllBookmarks();
+ }
+
+ public static void ReloadAllBookmarks(bool disposeFileWatchers = true)
+ {
+ _cachedBookmarks.Clear();
+ if (disposeFileWatchers)
+ DisposeFileWatchers();
+ LoadBookmarksIfEnabled();
+ }
+
+ public string GetTranslatedPluginTitle()
+ {
+ return _context.API.GetTranslation("flowlauncher_plugin_browserbookmark_plugin_name");
+ }
+
+ public string GetTranslatedPluginDescription()
+ {
+ return _context.API.GetTranslation("flowlauncher_plugin_browserbookmark_plugin_description");
+ }
+
+ public Control CreateSettingPanel()
+ {
+ return new SettingsControl(_settings);
+ }
+
+ public List LoadContextMenus(Result selectedResult)
+ {
+ return new List()
+ {
+ new Result
+ {
+ Title = _context.API.GetTranslation("flowlauncher_plugin_browserbookmark_copyurl_title"),
+ SubTitle = _context.API.GetTranslation("flowlauncher_plugin_browserbookmark_copyurl_subtitle"),
+ Action = _ =>
+ {
+ try
+ {
+ _context.API.CopyToClipboard(((BookmarkAttributes)selectedResult.ContextData).Url);
+
+ return true;
+ }
+ catch (Exception e)
+ {
+ var message = "Failed to set url in clipboard";
+ Log.Exception("Main", message, e, "LoadContextMenus");
+
+ _context.API.ShowMsg(message);
+
+ return false;
+ }
+ },
+ IcoPath = @"Images\copylink.png",
+ Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue8c8")
+ }
+ };
+ }
+
+ internal class BookmarkAttributes
+ {
+ internal string Url { get; set; }
+ }
+
+ public void Dispose()
+ {
+ DisposeFileWatchers();
+ }
+
+ private static void DisposeFileWatchers()
+ {
+ foreach (var watcher in Watchers)
+ {
+ watcher.Dispose();
+ }
+ Watchers.Clear();
+ }
}
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Bookmark.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Bookmark.cs
index c2fa9d977..c738da389 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Bookmark.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Bookmark.cs
@@ -1,22 +1,21 @@
using System.Collections.Generic;
-namespace Flow.Launcher.Plugin.BrowserBookmark.Models
+namespace Flow.Launcher.Plugin.BrowserBookmark.Models;
+
+// Source may be important in the future
+public record Bookmark(string Name, string Url, string Source = "")
{
- // Source may be important in the future
- public record Bookmark(string Name, string Url, string Source = "")
+ public override int GetHashCode()
{
- public override int GetHashCode()
- {
- var hashName = Name?.GetHashCode() ?? 0;
- var hashUrl = Url?.GetHashCode() ?? 0;
- return hashName ^ hashUrl;
- }
-
- public virtual bool Equals(Bookmark other)
- {
- return other != null && Name == other.Name && Url == other.Url;
- }
-
- public List CustomBrowsers { get; set; }= new();
+ var hashName = Name?.GetHashCode() ?? 0;
+ var hashUrl = Url?.GetHashCode() ?? 0;
+ return hashName ^ hashUrl;
}
-}
\ No newline at end of file
+
+ public virtual bool Equals(Bookmark other)
+ {
+ return other != null && Name == other.Name && Url == other.Url;
+ }
+
+ public List CustomBrowsers { get; set; } = new();
+}
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/CustomBrowser.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/CustomBrowser.cs
index 69bb56e48..74e0f299a 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/CustomBrowser.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/CustomBrowser.cs
@@ -1,45 +1,44 @@
-namespace Flow.Launcher.Plugin.BrowserBookmark.Models
-{
- public class CustomBrowser : BaseModel
- {
- private string _name;
- private string _dataDirectoryPath;
- private BrowserType browserType = BrowserType.Chromium;
+namespace Flow.Launcher.Plugin.BrowserBookmark.Models;
- public string Name
+public class CustomBrowser : BaseModel
+{
+ private string _name;
+ private string _dataDirectoryPath;
+ private BrowserType _browserType = BrowserType.Chromium;
+
+ public string Name
+ {
+ get => _name;
+ set
{
- get => _name;
- set
- {
- _name = value;
- OnPropertyChanged(nameof(Name));
- }
- }
-
- public string DataDirectoryPath
- {
- get => _dataDirectoryPath;
- set
- {
- _dataDirectoryPath = value;
- OnPropertyChanged(nameof(DataDirectoryPath));
- }
- }
-
- public BrowserType BrowserType
- {
- get => browserType;
- set
- {
- browserType = value;
- OnPropertyChanged(nameof(BrowserType));
- }
+ _name = value;
+ OnPropertyChanged();
}
}
- public enum BrowserType
+ public string DataDirectoryPath
{
- Chromium,
- Firefox,
+ get => _dataDirectoryPath;
+ set
+ {
+ _dataDirectoryPath = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public BrowserType BrowserType
+ {
+ get => _browserType;
+ set
+ {
+ _browserType = value;
+ OnPropertyChanged();
+ }
}
}
+
+public enum BrowserType
+{
+ Chromium,
+ Firefox,
+}
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Settings.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Settings.cs
index dc1016b4e..86532d275 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Settings.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Settings.cs
@@ -1,17 +1,16 @@
using System.Collections.ObjectModel;
-namespace Flow.Launcher.Plugin.BrowserBookmark.Models
+namespace Flow.Launcher.Plugin.BrowserBookmark.Models;
+
+public class Settings : BaseModel
{
- public class Settings : BaseModel
- {
- public bool OpenInNewBrowserWindow { get; set; } = true;
+ public bool OpenInNewBrowserWindow { get; set; } = true;
- public string BrowserPath { get; set; }
+ public string BrowserPath { get; set; }
- public bool LoadChromeBookmark { get; set; } = true;
- public bool LoadFirefoxBookmark { get; set; } = true;
- public bool LoadEdgeBookmark { get; set; } = true;
+ public bool LoadChromeBookmark { get; set; } = true;
+ public bool LoadFirefoxBookmark { get; set; } = true;
+ public bool LoadEdgeBookmark { get; set; } = true;
- public ObservableCollection CustomChromiumBrowsers { get; set; } = new();
- }
-}
\ No newline at end of file
+ public ObservableCollection CustomChromiumBrowsers { get; set; } = new();
+}
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/CustomBrowserSetting.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/CustomBrowserSetting.xaml
index 392c9e0a7..f5017ced5 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/CustomBrowserSetting.xaml
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/CustomBrowserSetting.xaml
@@ -63,7 +63,6 @@
+/// Interaction logic for CustomBrowserSetting.xaml
+///
+public partial class CustomBrowserSettingWindow : Window
{
- ///
- /// Interaction logic for CustomBrowserSetting.xaml
- ///
- public partial class CustomBrowserSettingWindow : Window
+ private CustomBrowser _currentCustomBrowser;
+ public CustomBrowserSettingWindow(CustomBrowser browser)
{
- private CustomBrowser currentCustomBrowser;
- public CustomBrowserSettingWindow(CustomBrowser browser)
+ InitializeComponent();
+ _currentCustomBrowser = browser;
+ DataContext = new CustomBrowser
{
- InitializeComponent();
- currentCustomBrowser = browser;
- DataContext = new CustomBrowser
- {
- Name = browser.Name,
- DataDirectoryPath = browser.DataDirectoryPath,
- BrowserType = browser.BrowserType,
- };
- }
-
- private void ConfirmEditCustomBrowser(object sender, RoutedEventArgs e)
- {
- CustomBrowser editBrowser = (CustomBrowser)DataContext;
- currentCustomBrowser.Name = editBrowser.Name;
- currentCustomBrowser.DataDirectoryPath = editBrowser.DataDirectoryPath;
- currentCustomBrowser.BrowserType = editBrowser.BrowserType;
- DialogResult = true;
- Close();
- }
+ Name = browser.Name,
+ DataDirectoryPath = browser.DataDirectoryPath,
+ BrowserType = browser.BrowserType,
+ };
+ }
- private void CancelEditCustomBrowser(object sender, RoutedEventArgs e)
- {
- Close();
- }
+ private void ConfirmEditCustomBrowser(object sender, RoutedEventArgs e)
+ {
+ CustomBrowser editBrowser = (CustomBrowser)DataContext;
+ _currentCustomBrowser.Name = editBrowser.Name;
+ _currentCustomBrowser.DataDirectoryPath = editBrowser.DataDirectoryPath;
+ _currentCustomBrowser.BrowserType = editBrowser.BrowserType;
+ DialogResult = true;
+ Close();
+ }
- private void WindowKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
- {
- if (e.Key == Key.Enter)
- {
- ConfirmEditCustomBrowser(sender, e);
- }
- }
+ private void CancelEditCustomBrowser(object sender, RoutedEventArgs e)
+ {
+ Close();
+ }
- private void OnSelectPathClick(object sender, RoutedEventArgs e)
+ private void WindowKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
+ {
+ if (e.Key == Key.Enter)
{
- var dialog = new FolderBrowserDialog();
- dialog.ShowDialog();
- CustomBrowser editBrowser = (CustomBrowser)DataContext;
- editBrowser.DataDirectoryPath = dialog.SelectedPath;
+ ConfirmEditCustomBrowser(sender, e);
}
}
+
+ private void OnSelectPathClick(object sender, RoutedEventArgs e)
+ {
+ var dialog = new FolderBrowserDialog();
+ dialog.ShowDialog();
+ CustomBrowser editBrowser = (CustomBrowser)DataContext;
+ editBrowser.DataDirectoryPath = dialog.SelectedPath;
+ }
}
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml.cs
index 2947c2cb5..4bdab89a9 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml.cs
@@ -4,119 +4,116 @@ using System.Windows.Input;
using System.ComponentModel;
using System.Threading.Tasks;
-namespace Flow.Launcher.Plugin.BrowserBookmark.Views
+namespace Flow.Launcher.Plugin.BrowserBookmark.Views;
+
+public partial class SettingsControl : INotifyPropertyChanged
{
- public partial class SettingsControl : INotifyPropertyChanged
+ public Settings Settings { get; }
+
+ public CustomBrowser SelectedCustomBrowser { get; set; }
+
+ public bool LoadChromeBookmark
{
- public Settings Settings { get; }
-
- public CustomBrowser SelectedCustomBrowser { get; set; }
-
- public bool LoadChromeBookmark
+ get => Settings.LoadChromeBookmark;
+ set
{
- get => Settings.LoadChromeBookmark;
- set
+ Settings.LoadChromeBookmark = value;
+ _ = Task.Run(() => Main.ReloadAllBookmarks());
+ }
+ }
+
+ public bool LoadFirefoxBookmark
+ {
+ get => Settings.LoadFirefoxBookmark;
+ set
+ {
+ Settings.LoadFirefoxBookmark = value;
+ _ = Task.Run(() => Main.ReloadAllBookmarks());
+ }
+ }
+
+ public bool LoadEdgeBookmark
+ {
+ get => Settings.LoadEdgeBookmark;
+ set
+ {
+ Settings.LoadEdgeBookmark = value;
+ _ = Task.Run(() => Main.ReloadAllBookmarks());
+ }
+ }
+
+ public bool OpenInNewBrowserWindow
+ {
+ get => Settings.OpenInNewBrowserWindow;
+ set
+ {
+ Settings.OpenInNewBrowserWindow = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(OpenInNewBrowserWindow)));
+ }
+ }
+
+ public SettingsControl(Settings settings)
+ {
+ Settings = settings;
+ InitializeComponent();
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ private void NewCustomBrowser(object sender, RoutedEventArgs e)
+ {
+ var newBrowser = new CustomBrowser();
+ var window = new CustomBrowserSettingWindow(newBrowser);
+ window.ShowDialog();
+ if (newBrowser is not
{
- Settings.LoadChromeBookmark = value;
- _ = Task.Run(() => Main.ReloadAllBookmarks());
- }
- }
-
- public bool LoadFirefoxBookmark
+ Name: null,
+ DataDirectoryPath: null
+ })
{
- get => Settings.LoadFirefoxBookmark;
- set
- {
- Settings.LoadFirefoxBookmark = value;
- _ = Task.Run(() => Main.ReloadAllBookmarks());
- }
+ Settings.CustomChromiumBrowsers.Add(newBrowser);
+ _ = Task.Run(() => Main.ReloadAllBookmarks());
}
+ }
- public bool LoadEdgeBookmark
+ private void DeleteCustomBrowser(object sender, RoutedEventArgs e)
+ {
+ if (CustomBrowsers.SelectedItem is CustomBrowser selectedCustomBrowser)
{
- get => Settings.LoadEdgeBookmark;
- set
- {
- Settings.LoadEdgeBookmark = value;
- _ = Task.Run(() => Main.ReloadAllBookmarks());
- }
+ Settings.CustomChromiumBrowsers.Remove(selectedCustomBrowser);
+ _ = Task.Run(() => Main.ReloadAllBookmarks());
}
+ }
- public bool OpenInNewBrowserWindow
+ private void MouseDoubleClickOnSelectedCustomBrowser(object sender, MouseButtonEventArgs e)
+ {
+ EditSelectedCustomBrowser();
+ }
+
+ private void Others_Click(object sender, RoutedEventArgs e)
+ {
+ CustomBrowsersList.Visibility = CustomBrowsersList.Visibility switch
{
- get => Settings.OpenInNewBrowserWindow;
- set
- {
- Settings.OpenInNewBrowserWindow = value;
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(OpenInNewBrowserWindow)));
- }
- }
+ Visibility.Collapsed => Visibility.Visible,
+ _ => Visibility.Collapsed
+ };
+ }
- public SettingsControl(Settings settings)
+ private void EditCustomBrowser(object sender, RoutedEventArgs e)
+ {
+ EditSelectedCustomBrowser();
+ }
+
+ private void EditSelectedCustomBrowser()
+ {
+ if (SelectedCustomBrowser is null)
+ return;
+
+ var window = new CustomBrowserSettingWindow(SelectedCustomBrowser);
+ var result = window.ShowDialog() ?? false;
+ if (result)
{
- Settings = settings;
- InitializeComponent();
- }
-
- public event PropertyChangedEventHandler PropertyChanged;
-
- private void NewCustomBrowser(object sender, RoutedEventArgs e)
- {
- var newBrowser = new CustomBrowser();
- var window = new CustomBrowserSettingWindow(newBrowser);
- window.ShowDialog();
- if (newBrowser is not
- {
- Name: null,
- DataDirectoryPath: null
- })
- {
- Settings.CustomChromiumBrowsers.Add(newBrowser);
- _ = Task.Run(() => Main.ReloadAllBookmarks());
- }
- }
-
- private void DeleteCustomBrowser(object sender, RoutedEventArgs e)
- {
- if (CustomBrowsers.SelectedItem is CustomBrowser selectedCustomBrowser)
- {
- Settings.CustomChromiumBrowsers.Remove(selectedCustomBrowser);
- _ = Task.Run(() => Main.ReloadAllBookmarks());
- }
- }
-
- private void MouseDoubleClickOnSelectedCustomBrowser(object sender, MouseButtonEventArgs e)
- {
- EditSelectedCustomBrowser();
- }
-
- private void Others_Click(object sender, RoutedEventArgs e)
- {
-
- if (CustomBrowsersList.Visibility == Visibility.Collapsed)
- {
- CustomBrowsersList.Visibility = Visibility.Visible;
- }
- else
- CustomBrowsersList.Visibility = Visibility.Collapsed;
- }
-
- private void EditCustomBrowser(object sender, RoutedEventArgs e)
- {
- EditSelectedCustomBrowser();
- }
-
- private void EditSelectedCustomBrowser()
- {
- if (SelectedCustomBrowser is null)
- return;
-
- var window = new CustomBrowserSettingWindow(SelectedCustomBrowser);
- var result = window.ShowDialog() ?? false;
- if (result)
- {
- _ = Task.Run(() => Main.ReloadAllBookmarks());
- }
+ _ = Task.Run(() => Main.ReloadAllBookmarks());
}
}
}
diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json
index 0778b7ae5..c4a24a57b 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json
@@ -4,7 +4,7 @@
"Name": "Browser Bookmarks",
"Description": "Search your browser bookmarks",
"Author": "qianlifeng, Ioannis G.",
- "Version": "3.1.5",
+ "Version": "3.2.0",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.BrowserBookmark.dll",
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json
index fac4621b0..60c1b11b5 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json
@@ -10,7 +10,7 @@
"Name": "Explorer",
"Description": "Find and manage files and folders via Windows Search or Everything",
"Author": "Jeremy Wu",
- "Version": "3.1.5",
+ "Version": "3.1.6",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Explorer.dll",
diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml
index fd6b09159..749c7c3ac 100644
--- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml
+++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml
@@ -30,9 +30,9 @@
请检查您是否可以连接到 github.com。这个错误意味着您可能无法安装或更新插件。
更新所有插件
您想要更新所有插件吗?
- Would you like to update {0} plugins?{1}Flow Launcher will restart after updating all plugins.
- Would you like to update {0} plugins?
- {0} plugins successfully updated. Restarting Flow, please wait...
+ 您想要更新 {0} 插件吗?{1}更新所有插件后 Flow Launcher 将重启。
+ 您想要更新 {0} 插件吗?
+ 插件 {0} 更新成功。正在重新启动 Flow Launcher,请稍候..……
插件{0}更新成功。正在重新启动 Flow Launcher,请稍候...
从未知源安装
您正在从未知源安装此插件,它可能包含潜在风险!{0}{0}请确保您了解来源以及安全性。{0}{0}您想要继续吗?{0}{0}(您可以通过设置关闭此警告)
@@ -40,7 +40,7 @@
成功安装插件{0}。请重新启动 Flow Launcher。
成功卸载插件{0}。请重新启动 Flow Launcher。
成功更新插件{0}。请重新启动 Flow Launcher。
- {0} plugins successfully updated. Please restart Flow.
+ 插件 {0} 更新成功。请重新启动 Flow Launcher。
插件 {0} 已被修改。请在进行任何进一步更改之前重新启动Flow。
diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json
index 4e475f171..438a407e2 100644
--- a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json
+++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json
@@ -6,7 +6,7 @@
"Name": "Plugins Manager",
"Description": "Management of installing, uninstalling or updating Flow Launcher plugins",
"Author": "Jeremy Wu",
- "Version": "3.1.0",
+ "Version": "3.1.1",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll",
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/ar.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/ar.xaml
index e62854305..dda427996 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/ar.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/ar.xaml
@@ -71,6 +71,7 @@
Run As Administrator
Open containing folder
Disable this program from displaying
+ Open target folder
Program
Search programs in Flow Launcher
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/cs.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/cs.xaml
index 7be93ad14..9a703d3de 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/cs.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/cs.xaml
@@ -71,6 +71,7 @@
Spustit jako správce
Otevřít umístění složky
Zakázat zobrazování tohoto programu
+ Open target folder
Program
Vyhledávání programů ve Flow Launcheru
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/da.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/da.xaml
index 905f7e787..676eb76cb 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/da.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/da.xaml
@@ -71,6 +71,7 @@
Run As Administrator
Open containing folder
Disable this program from displaying
+ Open target folder
Program
Search programs in Flow Launcher
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/de.xaml
index 5ab3eb9ec..293dbc9c8 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/de.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/de.xaml
@@ -71,6 +71,7 @@
Als Administrator ausführen
Enthaltenden Ordner öffnen
Disable this program from displaying
+ Open target folder
Programm
Suche Programme mit Flow Launcher
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml
index 39807db2c..d7e87b50b 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml
@@ -73,6 +73,7 @@
Run As Administrator
Open containing folder
Disable this program from displaying
+ Open target folder
Program
Search programs in Flow Launcher
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/es-419.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/es-419.xaml
index 4a18143bf..375dcd041 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/es-419.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/es-419.xaml
@@ -71,6 +71,7 @@
Run As Administrator
Open containing folder
Disable this program from displaying
+ Open target folder
Program
Search programs in Flow Launcher
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/es.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/es.xaml
index 4955c4ea2..d4eccc8ac 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/es.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/es.xaml
@@ -71,6 +71,7 @@
Ejecutar como administrador
Abrir carpeta contenedora
Desactivar la visualización de este programa
+ Abrir carpeta de destino
Programa
Busca programas en Flow Launcher
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/fr.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/fr.xaml
index 91f5e3ee5..d8b3dc5dd 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/fr.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/fr.xaml
@@ -71,6 +71,7 @@
Exécuter en tant qu'administrateur
Ouvrir le répertoire
Désactiver l'affichage de ce programme
+ Ouvrir le répertoire cible
Programme
Rechercher des programmes dans Flow Launcher
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/it.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/it.xaml
index d13f926d6..3a4d0e238 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/it.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/it.xaml
@@ -71,6 +71,7 @@
Run As Administrator
Apri percorso file
Disable this program from displaying
+ Open target folder
Program
Search programs in Flow Launcher
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/ja.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/ja.xaml
index 863735769..6c35d7df1 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/ja.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/ja.xaml
@@ -71,6 +71,7 @@
Run As Administrator
Open containing folder
Disable this program from displaying
+ Open target folder
Program
Search programs in Flow Launcher
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/ko.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/ko.xaml
index 9741a7fe2..2857194c7 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/ko.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/ko.xaml
@@ -71,6 +71,7 @@
관리자 권한으로 실행
포함된 폴더 열기
이 프로그램 표시 비활성화
+ Open target folder
프로그램
Flow Launcher에서 프로그램을 검색합니다
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/nb.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/nb.xaml
index e62854305..dda427996 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/nb.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/nb.xaml
@@ -71,6 +71,7 @@
Run As Administrator
Open containing folder
Disable this program from displaying
+ Open target folder
Program
Search programs in Flow Launcher
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/nl.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/nl.xaml
index 3c3b20afd..972cbe8d6 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/nl.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/nl.xaml
@@ -71,6 +71,7 @@
Run As Administrator
Open containing folder
Disable this program from displaying
+ Open target folder
Program
Search programs in Flow Launcher
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/pl.xaml
index c46a92006..d651fb0b9 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/pl.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/pl.xaml
@@ -71,6 +71,7 @@
Uruchom jako administrator
Open containing folder
Disable this program from displaying
+ Open target folder
Programy
Szukaj i uruchamiaj programy z poziomu Flow Launchera
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-br.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-br.xaml
index b943ffe97..f1cb7d7a2 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-br.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-br.xaml
@@ -71,6 +71,7 @@
Run As Administrator
Open containing folder
Disable this program from displaying
+ Open target folder
Program
Search programs in Flow Launcher
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-pt.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-pt.xaml
index 669fe3182..0832c2150 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-pt.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-pt.xaml
@@ -71,6 +71,7 @@
Executar como administrador
Abrir pasta de destino
Desativar exibição deste programa
+ Abrir pasta de destino
Programas
Pesquisa de programas com o Flow Launcher
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/ru.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/ru.xaml
index 9fdb70d4f..ebc30e161 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/ru.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/ru.xaml
@@ -71,6 +71,7 @@
Запустить от имени администратора
Открыть содержащую папку
Отключить отображение этой программы
+ Open target folder
Программа
Поиск программ в Flow Launcher
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/sk.xaml
index ee62a823e..a669d256b 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/sk.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/sk.xaml
@@ -71,6 +71,7 @@
Spustiť ako správca
Otvoriť umiestnenie priečinka
Zakázať zobrazovanie tohto programu
+ Otvoriť cieľový pričinok
Program
Vyhľadávanie programov vo Flow Launcheri
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/sr.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/sr.xaml
index f77bce039..6d995e1ab 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/sr.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/sr.xaml
@@ -71,6 +71,7 @@
Run As Administrator
Open containing folder
Disable this program from displaying
+ Open target folder
Program
Search programs in Flow Launcher
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/tr.xaml
index 3d1f087f2..675c9f132 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/tr.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/tr.xaml
@@ -71,6 +71,7 @@
Yönetici Olarak Çalıştır
Open containing folder
Disable this program from displaying
+ Open target folder
Program
Programları Flow Launcher'tan arayın
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/uk-UA.xaml
index f4a9926e8..344695d1e 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/uk-UA.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/uk-UA.xaml
@@ -71,6 +71,7 @@
Запустити від імені адміністратора
Відкрити папку
Вимкнути відображення цієї програми
+ Відкрити цільову папку
Програма
Пошук програм у Flow Launcher
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-cn.xaml
index 3a6fbda5d..324bd3057 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-cn.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-cn.xaml
@@ -71,6 +71,7 @@
以管理员身份运行
打开文件所在文件夹
禁止显示该程序
+ Open target folder
程序
在 Flow Launcher 中搜索程序
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-tw.xaml
index d27b5f78f..7c72b1aa3 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-tw.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-tw.xaml
@@ -71,6 +71,7 @@
以系統管理員身分執行
開啟檔案位置
Disable this program from displaying
+ Open target folder
程式
在 Flow Launcher 中搜尋程式
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs
index c02573ed8..e0a7f23de 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs
@@ -84,7 +84,7 @@ namespace Flow.Launcher.Plugin.Program
Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload win32 programs <{_win32s.Length}>");
Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload uwps <{_uwps.Length}>");
- bool cacheEmpty = !_win32s.Any() && !_uwps.Any();
+ bool cacheEmpty = !_win32s.Any() || !_uwps.Any();
if (cacheEmpty || _settings.LastIndexTime.AddHours(30) < DateTime.Now)
{
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs
index a7f447598..a64a708ef 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs
@@ -39,15 +39,20 @@ namespace Flow.Launcher.Plugin.Program.Programs
public string FullPath { get; set; }
///
- /// Path of the executable for .lnk, or the URL for .url. Arguments are included if any.
+ /// Path of the executable for .lnk, or the URL for .url
///
public string LnkResolvedPath { get; set; }
///
- /// Path of the actual executable file. Args are included.
+ /// Path of the actual executable file
///
public string ExecutablePath => LnkResolvedPath ?? FullPath;
+ ///
+ /// Arguments for the executable.
+ ///
+ public string Args { get; set; }
+
public string ParentDirectory { get; set; }
///
@@ -178,6 +183,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
Score = matchResult.Score,
TitleHighlightData = matchResult.MatchData,
ContextData = this,
+ TitleToolTip = $"{title}\n{ExecutablePath}",
Action = c =>
{
// Ctrl + Enter to open containing folder
@@ -261,11 +267,29 @@ namespace Flow.Launcher.Plugin.Program.Programs
},
IcoPath = "Images/folder.png",
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\xe838"),
- }
+ },
};
+ if (Extension(FullPath) == ShortcutExtension)
+ {
+ contextMenus.Add(OpenTargetFolderContextMenuResult(api));
+ }
return contextMenus;
}
+ private Result OpenTargetFolderContextMenuResult(IPublicAPI api)
+ {
+ return new Result
+ {
+ Title = api.GetTranslation("flowlauncher_plugin_program_open_target_folder"),
+ Action = _ =>
+ {
+ api.OpenDirectory(Path.GetDirectoryName(ExecutablePath), ExecutablePath);
+ return true;
+ },
+ IcoPath = "Images/folder.png",
+ Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\xe8de"),
+ };
+ }
public override string ToString()
{
@@ -327,7 +351,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
var args = _helper.arguments;
if (!string.IsNullOrEmpty(args))
{
- program.LnkResolvedPath += " " + args;
+ program.Args = args;
}
var description = _helper.description;
@@ -624,7 +648,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
private static IEnumerable ProgramsHasher(IEnumerable programs)
{
var startMenuPaths = GetStartMenuPaths();
- return programs.GroupBy(p => p.ExecutablePath.ToLowerInvariant())
+ return programs.GroupBy(p => (p.ExecutablePath + p.Args).ToLowerInvariant())
.AsParallel()
.SelectMany(g =>
{
diff --git a/Plugins/Flow.Launcher.Plugin.Program/plugin.json b/Plugins/Flow.Launcher.Plugin.Program/plugin.json
index 7f6b5f938..9ed7edbd9 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/plugin.json
+++ b/Plugins/Flow.Launcher.Plugin.Program/plugin.json
@@ -4,7 +4,7 @@
"Name": "Program",
"Description": "Search programs in Flow.Launcher",
"Author": "qianlifeng",
- "Version": "3.2.0",
+ "Version": "3.2.1",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Program.dll",
diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-cn.xaml
index ace554327..2db287e39 100644
--- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-cn.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-cn.xaml
@@ -2,7 +2,7 @@
替换 Win+R
- Close Command Prompt after pressing any key
+ 按下任意键后关闭命令提示符
按下任意键以关闭此窗口...
执行后不关闭命令窗口
始终以管理员身份运行
diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs
index f3c34d41d..921c6bc21 100644
--- a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs
+++ b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs
@@ -275,6 +275,8 @@ namespace Flow.Launcher.Plugin.Shell
info.FileName = command;
}
+ info.UseShellExecute = true;
+
break;
}
default:
diff --git a/Plugins/Flow.Launcher.Plugin.Shell/plugin.json b/Plugins/Flow.Launcher.Plugin.Shell/plugin.json
index 6c2b2a184..e06c22c54 100644
--- a/Plugins/Flow.Launcher.Plugin.Shell/plugin.json
+++ b/Plugins/Flow.Launcher.Plugin.Shell/plugin.json
@@ -4,7 +4,7 @@
"Name": "Shell",
"Description": "Provide executing commands from Flow Launcher",
"Author": "qianlifeng",
- "Version": "3.2.0",
+ "Version": "3.2.1",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Shell.dll",
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ar.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ar.xaml
index 41e005894..ddf8a4d71 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ar.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ar.xaml
@@ -17,7 +17,7 @@
Open Recycle Bin
Exit
Save Settings
- Restart Flow Launcher"
+ Restart Flow Launcher
Settings
Reload Plugin Data
Check For Update
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/cs.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/cs.xaml
index 3e053b39f..b377ad3d2 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/cs.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/cs.xaml
@@ -17,7 +17,7 @@
Open Recycle Bin
Ukončit
Save Settings
- Restart Flow Launcher"
+ Restartovat Flow Launcher
Nastavení
Znovu načíst data pluginů
Check For Update
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/da.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/da.xaml
index 8c5e4d0ff..30beff7b5 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/da.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/da.xaml
@@ -17,7 +17,7 @@
Open Recycle Bin
Afslut
Save Settings
- Restart Flow Launcher"
+ Restart Flow Launcher
Indstillinger
Reload Plugin Data
Check For Update
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml
index 282a0e809..91f32a844 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml
@@ -19,7 +19,7 @@
Open Recycle Bin
Exit
Save Settings
- Restart Flow Launcher"
+ Restart Flow Launcher
Settings
Reload Plugin Data
Check For Update
@@ -62,4 +62,4 @@
System Commands
Provides System related commands. e.g. shutdown, lock, settings etc.
-
\ No newline at end of file
+
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/es-419.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/es-419.xaml
index 05a5458e1..1275f8b74 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/es-419.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/es-419.xaml
@@ -17,7 +17,7 @@
Open Recycle Bin
Salir
Save Settings
- Restart Flow Launcher"
+ Restart Flow Launcher
Ajustes
Reload Plugin Data
Check For Update
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/es.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/es.xaml
index f95e44dcf..c457b160c 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/es.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/es.xaml
@@ -17,7 +17,7 @@
Abrir papelera de reciclaje
Salir
Guardar configuración
- Reiniciar Flow Launcher"
+ Reinicia Flow Launcher
Configuración
Recargar datos del complemento
Buscar actualizaciones
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/it.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/it.xaml
index a70994188..62d455c07 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/it.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/it.xaml
@@ -17,7 +17,7 @@
Open Recycle Bin
Esci
Save Settings
- Restart Flow Launcher"
+ Riavvia Flow Launcher
Impostazioni
Ricarica i dati del plugin
Check For Update
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ja.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ja.xaml
index 9080cf314..bb00a0df6 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ja.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ja.xaml
@@ -17,7 +17,7 @@
Open Recycle Bin
終
Save Settings
- Restart Flow Launcher"
+ Flow Launcherを再起動する
設
プラグインデータのリロード
Check For Update
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ko.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ko.xaml
index 5389cbb38..e2962ff34 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ko.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ko.xaml
@@ -17,7 +17,7 @@
Open Recycle Bin
종료
Save Settings
- Restart Flow Launcher"
+ Flow Launcher 재시작
설정
플러그인 데이터 새로고
Check For Update
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/nb.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/nb.xaml
index 41e005894..ddf8a4d71 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/nb.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/nb.xaml
@@ -17,7 +17,7 @@
Open Recycle Bin
Exit
Save Settings
- Restart Flow Launcher"
+ Restart Flow Launcher
Settings
Reload Plugin Data
Check For Update
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml
index a71e28e98..00201fa0e 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml
@@ -17,7 +17,7 @@
Open Recycle Bin
Afsluiten
Save Settings
- Restart Flow Launcher"
+ Flow Launcher herstarten
Instellingen
Reload Plugin Data
Check For Update
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/pl.xaml
index 4549f193e..4c7592411 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/pl.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/pl.xaml
@@ -17,7 +17,7 @@
Open Recycle Bin
Wyjdź
Save Settings
- Restart Flow Launcher"
+ Uruchom ponownie Flow Launchera
Ustawienia
Reload Plugin Data
Check For Update
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-br.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-br.xaml
index b80ddf621..a51c751df 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-br.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-br.xaml
@@ -17,7 +17,7 @@
Open Recycle Bin
Sair
Save Settings
- Restart Flow Launcher"
+ Reiniciar Flow Launcher
Configurações
Recarregar Dados de Plugin
Check For Update
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ru.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ru.xaml
index 927ccb0b6..3d3bcfc87 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ru.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ru.xaml
@@ -17,7 +17,7 @@
Open Recycle Bin
Выйти
Save Settings
- Restart Flow Launcher"
+ Restart Flow Launcher
Настройки
Перезагрузить данные плагинов
Check For Update
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/sk.xaml
index 82d8c2fe4..c5cee316f 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/sk.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/sk.xaml
@@ -17,7 +17,7 @@
Otvoriť kôš
Ukončiť
Uložiť nastavenia
- Reštartovať Flow Launcher"
+ Reštartovať Flow Launcher
Nastavenia
Znova načítať údaje pluginov
Skontrolovať aktualizácie
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/sr.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/sr.xaml
index fa6b54b81..a984954f9 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/sr.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/sr.xaml
@@ -17,7 +17,7 @@
Open Recycle Bin
Izlaz
Save Settings
- Restart Flow Launcher"
+ Restart Flow Launcher
Podešavanja
Reload Plugin Data
Check For Update
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/tr.xaml
index 9670d8297..35850cb26 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/tr.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/tr.xaml
@@ -17,7 +17,7 @@
Open Recycle Bin
Çıkı
Save Settings
- Restart Flow Launcher"
+ Flow Launcher'u Yeniden Başlat
Ayarlar
Reload Plugin Data
Check For Update
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/uk-UA.xaml
index 526144b43..8f2883bce 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/uk-UA.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/uk-UA.xaml
@@ -17,7 +17,7 @@
Відкрити кошик
Вийти
Зберегти налаштування
- Перезапустити Flow Launcher"
+ Перезапустити Flow Launcher
Налаштування
Перезавантажити дані плагінів
Перевірити наявність оновлень
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-cn.xaml
index be84d0e7f..cd0b6f166 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-cn.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-cn.xaml
@@ -11,20 +11,20 @@
Log Off/Sign Out
Lock
Sleep
- Hibernate
- Index Option
- Empty Recycle Bin
- Open Recycle Bin
+ 休眠
+ 索引选项
+ 清空回收站
+ 打开回收站
退出
- Save Settings
- Restart Flow Launcher"
+ 保存设置
+ 重启 Flow Launcher
设置
重新加载插件数据
- Check For Update
- Open Log Location
- Flow Launcher Tips
- Flow Launcher UserData Folder
- Toggle Game Mode
+ 检查更新
+ 打开日志文件夹
+ Flow Launcher 提示
+ Flow Launcher 用户数据文件夹
+ 切换游戏模式
关闭电脑
@@ -46,7 +46,7 @@
检查新的 Flow Launcher 更新
访问 Flow Launcher 的文档以获取更多帮助以及使用技巧
打开Flow Launcher 设置文件夹
- Toggle Game Mode
+ 切换游戏模式
成功
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-tw.xaml
index 62bf8f012..9934acc9f 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-tw.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-tw.xaml
@@ -17,7 +17,7 @@
Open Recycle Bin
結束
Save Settings
- Restart Flow Launcher"
+ 重新啟動 Flow Launcher
設定
重新載入插件資料
Check For Update
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/plugin.json b/Plugins/Flow.Launcher.Plugin.Sys/plugin.json
index 5276690e3..f7dea266a 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/plugin.json
+++ b/Plugins/Flow.Launcher.Plugin.Sys/plugin.json
@@ -4,7 +4,7 @@
"Name": "System Commands",
"Description": "Provide System related commands. e.g. shutdown,lock, setting etc.",
"Author": "qianlifeng",
- "Version": "3.1.1",
+ "Version": "3.1.2",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Sys.dll",
diff --git a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.pt-PT.resx b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.pt-PT.resx
index b243dafd8..45dc2c8cc 100644
--- a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.pt-PT.resx
+++ b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.pt-PT.resx
@@ -2443,7 +2443,7 @@
Change search options for files and folders
- Adjust settings before giving a presentation
+ Ajustar definições antes de uma apresentação
Digitalizar um documento ou imagem
@@ -2467,10 +2467,10 @@
Calibrate the screen for pen or touch input
- Manage user certificates
+ Gerir certificados do utilizador
- Schedule tasks
+ Agendar tarefas
Ignore repeated keystrokes using FilterKeys
diff --git a/Plugins/Flow.Launcher.Plugin.WindowsSettings/plugin.json b/Plugins/Flow.Launcher.Plugin.WindowsSettings/plugin.json
index 5e82a43c0..dc8bfa22c 100644
--- a/Plugins/Flow.Launcher.Plugin.WindowsSettings/plugin.json
+++ b/Plugins/Flow.Launcher.Plugin.WindowsSettings/plugin.json
@@ -4,7 +4,7 @@
"Description": "Search settings inside Control Panel and Settings App",
"Name": "Windows Settings",
"Author": "TobiasSekan",
- "Version": "4.0.6",
+ "Version": "4.0.7",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.WindowsSettings.dll",
diff --git a/README.md b/README.md
index 325dc9e4b..1a633de0d 100644
--- a/README.md
+++ b/README.md
@@ -16,77 +16,15 @@
-Dedicated to making your work flow more seamless. Search everything from applications, files, bookmarks, YouTube, Twitter and more. Flow will continue to evolve, designed to be open and built with the community at heart.
+A quick file search and app launcher for Windows with community-made plugins.
+
+
+Dedicated to making your work flow more seamless. Search everything from applications, files, bookmarks, YouTube, Twitter and more. Flow will continue to evolve, designed to be open and built with the community at heart.
Remember to star it, flow will love you more :)
-## 🎅 New Features🤶
-
-### Preview Panel
-
-
-
-- Use the F1 key to open/hide the preview panel.
-- Media files will be displayed as large images, otherwise a large icon and entire path will be displayed.
-- Turn on preview permanently via Settings (Always Preview).
-- Use hotkeys (Ctrl+Plus,Minus / Ctrl+],[) to adjust flow's search window width and height quickly if the preview area is too narrow.
-- This feature is currently in its early stages.
-
-### Everything Plugin Merged Into Explorer
-
-
-
-- Switch easily between Everything and Windows Search to take advantage of both search engines (remember to remove existing Everything plugin).
-- Use features available to both Everything and Explorer plugins
-
-### Date & Time Display In Search Window
-
-
-
-- Display the date and time when the search window is triggered.
-
-### Drag & Drop
-
-
-
-- Drag an item to Discord or computer location.
-- The target program determines whether the drop is to copy or move the item (can change via CTRL or Alt), and the operation is displayed on the mouse cursor.
-
-### Custom Shortcut
-
-
-
-
-- New shortcut functionality to set additional action keywords or search terms.
-
-### Improved Program Plugin
-
-- PATH is now indexed
-- Support for .url files, flow can now search installed steam/epic games.
-- Improved UWP indexing.
-
-### Improved Memory Usage
-
-- Fixed a memory leak and reduced overall memory usage.
-
-### Improved Plugin / Plugin Store
-
-- Search plugins in the Plugin Store and existing plugin tab.
-- Categorised sections in Plugin Store to easily see new and updated plugins.
-
-### Improved Non-C# Plugin's Panel Design
-
-
-
-- The design has been adjusted to align to the overall look and feel of flow.
-- Simplified the information displayed on buttons
-
-🚂[Full Changelogs](https://github.com/Flow-Launcher/Flow.Launcher/releases)
-
-
-
Getting Started •
Features •
@@ -104,15 +42,29 @@ Dedicated to making your work flow more seamless. Search everything from applica
### Installation
-| [Windows 7+ installer](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest/download/Flow-Launcher-Setup.exe) | [Portable](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest/download/Flow-Launcher-Portable.zip) |
-| :----------------------------------------------------------: | :----------------------------------------------------------: |
+[Windows 7+ Installer](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest/download/Flow-Launcher-Setup.exe) or [Portable Version](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest/download/Flow-Launcher-Portable.zip)
-| `winget install "Flow Launcher"` | `scoop install Flow-Launcher` | `choco install Flow-Launcher` |
-| :------------------------------: | :------------------------------: | :------------------------------: |
+#### Winget
+
+```
+winget install "Flow Launcher"
+```
+
+#### Scoop
+
+```
+scoop install Flow-Launcher
+```
+
+#### Chocolatey
+
+```
+choco install Flow-Launcher
+```
> When installing for the first time Windows may raise an issue about security due to code not being signed, if you downloaded from this repo then you are good to continue the set up.
-And you can download [early access version](https://github.com/Flow-Launcher/Prereleases/releases).
+Or download the [early access version](https://github.com/Flow-Launcher/Prereleases/releases).
@@ -123,6 +75,7 @@ And you can download [early access version](https://github.com/Flow-Launcher/Pre
- Search for apps, files or file contents.
+- Supports Everything and Windows Index.
@@ -156,7 +109,7 @@ And you can download [early access version](https://github.com/Flow-Launcher/Pre
- Run batch and PowerShell commands as Administrator or a different user.
-- Ctrl+Enter to Run as Administrator.
+- Ctrl +Shift +Enter to Run as Administrator.
### Explorer
@@ -164,6 +117,13 @@ And you can download [early access version](https://github.com/Flow-Launcher/Pre
- Save file or folder locations for quick access.
+#### Drag & Drop
+
+
+
+- Drag a file/folder to File Exlporer, or even Discord.
+- Copy/move behavior can be change via Ctrl or Shift , and the operation is displayed on the mouse cursor.
+
### Windows & Control Panel Settings
@@ -176,6 +136,16 @@ And you can download [early access version](https://github.com/Flow-Launcher/Pre
- Prioritise the order of each plugin's results.
+### Preview Panel
+
+
+
+- Use F1 to toggle the preview panel.
+- Media files will be displayed as large images, otherwise a large icon and full path will be displayed.
+- Turn on preview permanently via Settings (Always Preview).
+- Use Ctrl ++ /- and Ctrl +[ /] to adjust search window width and height quickly if the preview area is too narrow.
+
+
### Customizations
@@ -187,12 +157,48 @@ And you can download [early access version](https://github.com/Flow-Launcher/Pre
- There are various themes and you also can make your own.
-### 💬 Language
+#### Date & Time Display In Search Window
+
+
+
+- Display date and time in search window.
+
+### 💬 Languages
- Supports languages from Chinese to Italian and more.
-- Supports Pinyin search.
+- Supports Pinyin (拼音) search.
- [Crowdin](https://crowdin.com/project/flow-launcher) support for language translations.
+
+Supported languages
+
+ English
+ 中文
+ 中文(繁体)
+ Українська
+ Русский
+ Français
+ 日本語
+ Dutch
+ Polski
+ Dansk
+ de, Deutsch
+ ko, 한국어
+ Srpski
+ Português
+ Português (Brasil)
+ Spanish
+ es-419, Spanish (Latin America)
+ Italiano
+ Norsk Bokmål
+ Slovenčina
+ Türkçe
+ čeština
+ اللغة العربية
+ Tiếng Việt
+
+
+
### Portable
- Fully portable.
@@ -206,7 +212,7 @@ And you can download [early access version](https://github.com/Flow-Launcher/Pre
- Pause hotkey activation when you are playing games.
-- When in search window use Ctrl+F12 to toggle on/off.
+- When in search window use Ctrl +F12 to toggle on/off.
- Type `Toggle Game Mode`
@@ -257,6 +263,7 @@ And you can download [early access version](https://github.com/Flow-Launcher/Pre
### 🛒 Plugin Store
+
- You can view the full plugin list or quickly install a plugin via the Plugin Store menu inside Settings
@@ -267,26 +274,29 @@ And you can download [early access version](https://github.com/Flow-Launcher/Pre
## ⌨️ Hotkeys
-| Hotkey | Description |
-| ------------------------------------------------------------------ | ---------------------------------------------- |
-| Alt + Space | Open search window (default and configurable) |
-| Enter | Execute |
-| Ctrl +Shift +Enter | Run as admin |
-| ↑ ↓ | Scroll up & down |
-| ← → | Back to result / Open Context Menu |
-| Ctrl +O , Shift +Enter | Open Context Menu |
-| Tab | Autocomplete |
-| F1 | Toggle Preview Panel (default and configurable)|
-| Esc | Back to results / hide search window |
-| Ctrl +C | Copy the actual folder / file |
-| Ctrl +I | Open flow's settings |
-| Ctrl +R | Run the current query again (refresh results) |
-| F5 | Reload all plugin data |
-| Ctrl + F12 | Toggle Game Mode when in search window |
-| Ctrl + + ,- | Quickly change maximum results shown |
-| Ctrl + [ ,] | Quickly change search window width |
-| Ctrl + H | Open search history |
-| Ctrl + Backspace | Back to previous directory |
+| Hotkey | Description |
+| ------------------------------------------------------------------------- | ----------------------------------------------- |
+| Alt +Space | Open search window (default and configurable) |
+| Enter | Execute |
+| Ctrl +Enter | Open containing folder |
+| Ctrl +Shift +Enter | Run as admin |
+| ↑ /↓ , Shift +Tab /Tab | Previous / Next result |
+| ← /→ | Back to result / Open Context Menu |
+| Ctrl +O , Shift +Enter | Open Context Menu |
+| Ctrl +Tab | Autocomplete |
+| F1 | Toggle Preview Panel (default and configurable) |
+| Esc | Back to results / hide search window |
+| Ctrl +C | Copy folder / file |
+| Ctrl +Shift +C | Copy folder / file path |
+| Ctrl +I | Open Flow's settings |
+| Ctrl +R | Run the current query again (refresh results) |
+| F5 | Reload all plugin data |
+| Ctrl +F12 | Toggle Game Mode when in search window |
+| Ctrl ++ ,- | Adjust maximum results shown |
+| Ctrl +[ ,] | Adjust search window width |
+| Ctrl +H | Open search history |
+| Ctrl +Backspace | Back to previous directory |
+| PageUp /PageDown | Previous / Next Page |
## System Command List
@@ -313,8 +323,6 @@ And you can download [early access version](https://github.com/Flow-Launcher/Pre
| Flow Launcher UserData Folder | Open the location where Flow Launcher's settings are stored |
| Toggle Game Mode | Toggle Game Mode |
-
-
### 💁♂️ Tips
- [More tips](https://flowlauncher.com/docs/#/usage-tips)
diff --git a/appveyor.yml b/appveyor.yml
index f7912095c..c9ba17077 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,4 +1,4 @@
-version: '1.17.2.{build}'
+version: '1.18.0.{build}'
init:
- ps: |