diff --git a/.github/workflows/default_plugins.yml b/.github/workflows/default_plugins.yml index 83e830d75..381044c51 100644 --- a/.github/workflows/default_plugins.yml +++ b/.github/workflows/default_plugins.yml @@ -10,7 +10,7 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Setup .NET uses: actions/setup-dotnet@v5 with: diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 416c75a9d..0659ae645 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -20,7 +20,7 @@ jobs: NUGET_CERT_REVOCATION_MODE: offline BUILD_NUMBER: ${{ github.run_number }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set Flow.Launcher.csproj version id: update uses: vers-one/dotnet-project-version-updater@v1.7 @@ -54,28 +54,28 @@ jobs: shell: powershell run: .\Scripts\post_build.ps1 - name: Upload Plugin Nupkg - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: Plugin nupkg path: | Output\Release\Flow.Launcher.Plugin.*.nupkg compression-level: 0 - name: Upload Setup - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: Flow Installer path: | Output\Packages\Flow-Launcher-*.exe compression-level: 0 - name: Upload Portable Version - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: Portable Version path: | Output\Packages\Flow-Launcher-Portable.zip compression-level: 0 - name: Upload Full Nupkg - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: Full nupkg path: | @@ -83,7 +83,7 @@ jobs: compression-level: 0 - name: Upload Release Information - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: RELEASES path: | diff --git a/.github/workflows/pr_assignee.yml b/.github/workflows/pr_assignee.yml index 5be603df6..33098672b 100644 --- a/.github/workflows/pr_assignee.yml +++ b/.github/workflows/pr_assignee.yml @@ -14,4 +14,4 @@ jobs: runs-on: ubuntu-latest steps: - name: Assign PR to creator - uses: toshimaru/auto-author-assign@v2.1.1 + uses: toshimaru/auto-author-assign@v3.0.1 diff --git a/.github/workflows/release_pr.yml b/.github/workflows/release_pr.yml index 58a877ba3..c7e9a90a6 100644 --- a/.github/workflows/release_pr.yml +++ b/.github/workflows/release_pr.yml @@ -11,7 +11,7 @@ jobs: update-pr: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: actions/setup-python@v6 with: diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs index 148fd969e..470019143 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs @@ -100,11 +100,11 @@ namespace Flow.Launcher.Core.Plugin RPC = new JsonRpc(handler, new JsonRPCPublicAPI(Context.API)); - RPC.AddLocalRpcMethod("UpdateResults", new Action((rawQuery, response) => + RPC.AddLocalRpcMethod("UpdateResults", new Action((trimmedQuery, response) => { var results = ParseResults(response); ResultsUpdated?.Invoke(this, - new ResultUpdatedEventArgs { Query = new Query() { RawQuery = rawQuery }, Results = results }); + new ResultUpdatedEventArgs { Query = new Query() { TrimmedQuery = trimmedQuery }, Results = results }); })); RPC.SynchronizationContext = null; RPC.StartListening(); diff --git a/Flow.Launcher.Core/Plugin/PluginConfig.cs b/Flow.Launcher.Core/Plugin/PluginConfig.cs index 4bf12faff..db6813deb 100644 --- a/Flow.Launcher.Core/Plugin/PluginConfig.cs +++ b/Flow.Launcher.Core/Plugin/PluginConfig.cs @@ -6,6 +6,7 @@ using Flow.Launcher.Infrastructure; using Flow.Launcher.Plugin; using System.Text.Json; using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.Plugin.SharedCommands; namespace Flow.Launcher.Core.Plugin { @@ -30,7 +31,18 @@ namespace Flow.Launcher.Core.Plugin { try { - Directory.Delete(directory, true); + var fullyDeleted = FilesFolders.TryDeleteDirectoryRobust(directory, maxRetries: 3, retryDelayMs: 200); + if (!fullyDeleted) + { + PublicApi.Instance.LogWarn(ClassName, $"Directory <{directory}> was not fully deleted."); + + // Directory was not fully deleted, recreate the marker file so deletion will be retried on next startup + var markerFilePath = Path.Combine(directory, DataLocation.PluginDeleteFile); + if (!File.Exists(markerFilePath)) + { + File.WriteAllText(markerFilePath, string.Empty); + } + } } catch (Exception e) { diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index d3a8003c1..b808e2a7f 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -401,7 +401,7 @@ namespace Flow.Launcher.Core.Plugin { Title = Localize.pluginStillInitializing(metadata.Name), SubTitle = Localize.pluginStillInitializingSubtitle(), - AutoCompleteText = query.RawQuery, + AutoCompleteText = query.TrimmedQuery, IcoPath = metadata.IcoPath, PluginDirectory = metadata.PluginDirectory, ActionKeywordAssigned = query.ActionKeyword, @@ -443,7 +443,7 @@ namespace Flow.Launcher.Core.Plugin { Title = Localize.pluginFailedToRespond(metadata.Name), SubTitle = Localize.pluginFailedToRespondSubtitle(), - AutoCompleteText = query.RawQuery, + AutoCompleteText = query.TrimmedQuery, IcoPath = Constant.ErrorIcon, PluginDirectory = metadata.PluginDirectory, ActionKeywordAssigned = query.ActionKeyword, @@ -467,7 +467,7 @@ namespace Flow.Launcher.Core.Plugin { Title = Localize.pluginStillInitializing(metadata.Name), SubTitle = Localize.pluginStillInitializingSubtitle(), - AutoCompleteText = query.RawQuery, + AutoCompleteText = query.TrimmedQuery, IcoPath = metadata.IcoPath, PluginDirectory = metadata.PluginDirectory, ActionKeywordAssigned = query.ActionKeyword, @@ -931,6 +931,18 @@ namespace Flow.Launcher.Core.Plugin FilesFolders.CopyAll(pluginFolderPath, newPluginPath, (s) => PublicApi.Instance.ShowMsgBox(s)); + // Check if marker file exists and delete it + try + { + var markerFilePath = Path.Combine(newPluginPath, DataLocation.PluginDeleteFile); + if (File.Exists(markerFilePath)) + File.Delete(markerFilePath); + } + catch (Exception e) + { + PublicApi.Instance.LogException(ClassName, $"Failed to delete plugin marker file in {newPluginPath}", e); + } + try { if (Directory.Exists(tempFolderPluginPath)) diff --git a/Flow.Launcher.Core/Plugin/QueryBuilder.cs b/Flow.Launcher.Core/Plugin/QueryBuilder.cs index 25a32a728..aac620cce 100644 --- a/Flow.Launcher.Core/Plugin/QueryBuilder.cs +++ b/Flow.Launcher.Core/Plugin/QueryBuilder.cs @@ -6,15 +6,16 @@ namespace Flow.Launcher.Core.Plugin { public static class QueryBuilder { - public static Query Build(string text, Dictionary nonGlobalPlugins) + public static Query Build(string originalQuery, string trimmedQuery, Dictionary nonGlobalPlugins) { // home query - if (string.IsNullOrEmpty(text)) + if (string.IsNullOrEmpty(trimmedQuery)) { return new Query() { Search = string.Empty, - RawQuery = string.Empty, + OriginalQuery = string.Empty, + TrimmedQuery = string.Empty, SearchTerms = Array.Empty(), ActionKeyword = string.Empty, IsHomeQuery = true @@ -22,14 +23,13 @@ namespace Flow.Launcher.Core.Plugin } // replace multiple white spaces with one white space - var terms = text.Split(Query.TermSeparator, StringSplitOptions.RemoveEmptyEntries); + var terms = trimmedQuery.Split(Query.TermSeparator, StringSplitOptions.RemoveEmptyEntries); if (terms.Length == 0) { // nothing was typed return null; } - var rawQuery = text; string actionKeyword, search; string possibleActionKeyword = terms[0]; string[] searchTerms; @@ -38,21 +38,22 @@ namespace Flow.Launcher.Core.Plugin { // use non global plugin for query actionKeyword = possibleActionKeyword; - search = terms.Length > 1 ? rawQuery[(actionKeyword.Length + 1)..].TrimStart() : string.Empty; + search = terms.Length > 1 ? trimmedQuery[(actionKeyword.Length + 1)..].TrimStart() : string.Empty; searchTerms = terms[1..]; } else { // non action keyword actionKeyword = string.Empty; - search = rawQuery.TrimStart(); + search = trimmedQuery.TrimStart(); searchTerms = terms; } return new Query() { Search = search, - RawQuery = rawQuery, + OriginalQuery = originalQuery, + TrimmedQuery = trimmedQuery, SearchTerms = searchTerms, ActionKeyword = actionKeyword, IsHomeQuery = false diff --git a/Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs b/Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs index 4a3e6474e..53df05bf2 100644 --- a/Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs +++ b/Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs @@ -496,6 +496,8 @@ namespace Flow.Launcher.Infrastructure.DialogJump uint dwmsEventTime ) { + if (hwnd.IsNull) return; + await _foregroundChangeLock.WaitAsync(); try { @@ -647,6 +649,8 @@ namespace Flow.Launcher.Infrastructure.DialogJump uint dwmsEventTime ) { + if (hwnd.IsNull) return; + // If the dialog window is moved, update the Dialog Jump window position var dialogWindowExist = false; lock (_dialogWindowLock) @@ -672,6 +676,8 @@ namespace Flow.Launcher.Infrastructure.DialogJump uint dwmsEventTime ) { + if (hwnd.IsNull) return; + // If the dialog window is moved or resized, update the Dialog Jump window position if (_dragMoveTimer != null) { @@ -697,6 +703,8 @@ namespace Flow.Launcher.Infrastructure.DialogJump uint dwmsEventTime ) { + if (hwnd.IsNull) return; + // If the dialog window is destroyed, set _dialogWindowHandle to null var dialogWindowExist = false; lock (_dialogWindowLock) @@ -728,6 +736,8 @@ namespace Flow.Launcher.Infrastructure.DialogJump uint dwmsEventTime ) { + if (hwnd.IsNull) return; + // If the dialog window is hidden, set _dialogWindowHandle to null var dialogWindowExist = false; lock (_dialogWindowLock) @@ -759,6 +769,8 @@ namespace Flow.Launcher.Infrastructure.DialogJump uint dwmsEventTime ) { + if (hwnd.IsNull) return; + // If the dialog window is ended, set _dialogWindowHandle to null var dialogWindowExist = false; lock (_dialogWindowLock) diff --git a/Flow.Launcher.Infrastructure/NativeMethods.txt b/Flow.Launcher.Infrastructure/NativeMethods.txt index 979e91c5a..36d6be224 100644 --- a/Flow.Launcher.Infrastructure/NativeMethods.txt +++ b/Flow.Launcher.Infrastructure/NativeMethods.txt @@ -105,4 +105,8 @@ GetWindowThreadProcessId OpenProcess GetProcessId DuplicateTokenEx -CreateProcessWithTokenW \ No newline at end of file +CreateProcessWithTokenW + +DeviceNotifyCallbackRoutine + +MonitorFromWindow diff --git a/Flow.Launcher.Infrastructure/PinyinAlphabet.cs b/Flow.Launcher.Infrastructure/PinyinAlphabet.cs index 1c0cc6872..7f55d8909 100644 --- a/Flow.Launcher.Infrastructure/PinyinAlphabet.cs +++ b/Flow.Launcher.Infrastructure/PinyinAlphabet.cs @@ -27,7 +27,7 @@ namespace Flow.Launcher.Infrastructure { switch (e.PropertyName) { - case nameof (Settings.ShouldUsePinyin): + case nameof(Settings.ShouldUsePinyin): if (_settings.ShouldUsePinyin) { Reload(); @@ -52,7 +52,7 @@ namespace Flow.Launcher.Infrastructure private void CreateDoublePinyinTableFromStream(Stream jsonStream) { - var table = JsonSerializer.Deserialize>>(jsonStream) ?? + var table = JsonSerializer.Deserialize>>(jsonStream) ?? throw new InvalidOperationException("Failed to deserialize double pinyin table: result is null"); var schemaKey = _settings.DoublePinyinSchema.ToString(); @@ -128,12 +128,12 @@ namespace Flow.Launcher.Infrastructure if (IsChineseCharacter(content[i])) { var translated = _settings.UseDoublePinyin ? ToDoublePinyin(resultList[i]) : resultList[i]; - - if (i > 0) + + if (i > 0 && content[i - 1] != ' ') { resultBuilder.Append(' '); } - + map.AddNewIndex(resultBuilder.Length, translated.Length); resultBuilder.Append(translated); previousIsChinese = true; @@ -144,11 +144,14 @@ namespace Flow.Launcher.Infrastructure if (previousIsChinese) { previousIsChinese = false; - resultBuilder.Append(' '); + if (content[i] != ' ') + { + resultBuilder.Append(' '); + } } - - map.AddNewIndex(resultBuilder.Length, resultList[i].Length); - resultBuilder.Append(resultList[i]); + + map.AddNewIndex(resultBuilder.Length, 1); + resultBuilder.Append(content[i]); } } @@ -156,7 +159,7 @@ namespace Flow.Launcher.Infrastructure var translation = resultBuilder.ToString(); var result = (translation, map); - + return _pinyinCache[content] = result; } @@ -185,8 +188,8 @@ namespace Flow.Launcher.Infrastructure private string ToDoublePinyin(string fullPinyin) { - return currentDoublePinyinTable.TryGetValue(fullPinyin, out var doublePinyinValue) - ? doublePinyinValue + return currentDoublePinyinTable.TryGetValue(fullPinyin, out var doublePinyinValue) + ? doublePinyinValue : fullPinyin; } } diff --git a/Flow.Launcher.Infrastructure/TranslationMapping.cs b/Flow.Launcher.Infrastructure/TranslationMapping.cs index b4c6764df..e70443077 100644 --- a/Flow.Launcher.Infrastructure/TranslationMapping.cs +++ b/Flow.Launcher.Infrastructure/TranslationMapping.cs @@ -21,7 +21,7 @@ namespace Flow.Launcher.Infrastructure public int MapToOriginalIndex(int translatedIndex) { var searchResult = _originalToTranslated.BinarySearch(translatedIndex); - return searchResult >= 0 ? searchResult : ~searchResult; + return searchResult >= 0 ? searchResult + 1 : ~searchResult; } public void EndConstruct() diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 7cff670be..3950236ac 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -481,6 +481,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings } public bool LeaveCmdOpen { get; set; } public bool HideWhenDeactivated { get; set; } = true; + public bool ShowTaskbarWhenInvoked { get; set; } = false; public bool AlwaysRunAsAdministrator { get; set; } = false; diff --git a/Flow.Launcher.Infrastructure/Win32Helper.cs b/Flow.Launcher.Infrastructure/Win32Helper.cs index b210b149c..4c529c485 100644 --- a/Flow.Launcher.Infrastructure/Win32Helper.cs +++ b/Flow.Launcher.Infrastructure/Win32Helper.cs @@ -1177,5 +1177,32 @@ cleanup: } #endregion + + #region Taskbar + + public static unsafe void ShowTaskbar() + { + // Find the taskbar window + var taskbarHwnd = PInvoke.FindWindowEx(HWND.Null, HWND.Null, "Shell_TrayWnd", null); + if (taskbarHwnd == HWND.Null) return; + + // Magic from https://github.com/Oliviaophia/SmartTaskbar + const uint TrayBarFlag = 0x05D1; + var mon = PInvoke.MonitorFromWindow(taskbarHwnd, Windows.Win32.Graphics.Gdi.MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST); + PInvoke.PostMessage(taskbarHwnd, TrayBarFlag, new WPARAM(1), new LPARAM((nint)mon.Value)); + } + + public static void HideTaskbar() + { + // Find the taskbar window + var taskbarHwnd = PInvoke.FindWindowEx(HWND.Null, HWND.Null, "Shell_TrayWnd", null); + if (taskbarHwnd == HWND.Null) return; + + // Magic from https://github.com/Oliviaophia/SmartTaskbar + const uint TrayBarFlag = 0x05D1; + PInvoke.PostMessage(taskbarHwnd, TrayBarFlag, new WPARAM(0), IntPtr.Zero); + } + + #endregion } } diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 649d59ad7..378d4306a 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -15,10 +15,10 @@ - 5.1.0 - 5.1.0 - 5.1.0 - 5.1.0 + 5.2.0 + 5.2.0 + 5.2.0 + 5.2.0 Flow.Launcher.Plugin Flow-Launcher MIT @@ -72,9 +72,9 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Flow.Launcher.Plugin/Query.cs b/Flow.Launcher.Plugin/Query.cs index f50614699..79e6f7d62 100644 --- a/Flow.Launcher.Plugin/Query.cs +++ b/Flow.Launcher.Plugin/Query.cs @@ -1,4 +1,5 @@ -using System.Text.Json.Serialization; +using System; +using System.Text.Json.Serialization; namespace Flow.Launcher.Plugin { @@ -8,11 +9,29 @@ namespace Flow.Launcher.Plugin public class Query { /// - /// Raw query, this includes action keyword if it has. - /// It has handled buildin custom query shortkeys and build-in shortcuts, and it trims the whitespace. - /// We didn't recommend use this property directly. You should always use Search property. + /// Original query, exactly how the user has typed into the search box. + /// We don't recommend using this property directly. You should always use Search property. /// - public string RawQuery { get; internal init; } + public string OriginalQuery { get; internal init; } + + /// + /// Raw query, this includes action keyword if it has. + /// It has handled built-in custom query hotkeys and built-in shortcuts, and it trims the whitespace. + /// We don't recommend using this property directly. You should always use Search property. + /// + [Obsolete("RawQuery is renamed to TrimmedQuery. This property will be removed. Update the code to use TrimmedQuery instead.")] + public string RawQuery { + get => TrimmedQuery; + internal init { TrimmedQuery = value; } + } + + /// + /// Original query but with trimmed whitespace. Includes action keyword. + /// It has handled built-in custom query hotkeys and build-in shortcuts. + /// If you need the exact original query from the search box, use OriginalQuery property instead. + /// We don't recommend using this property directly. You should always use Search property. + /// + public string TrimmedQuery { get; internal init; } /// /// Determines whether the query was forced to execute again. @@ -28,7 +47,7 @@ namespace Flow.Launcher.Plugin /// /// Search part of a query. - /// This will not include action keyword if exclusive plugin gets it, otherwise it should be same as RawQuery. + /// This will not include action keyword if exclusive plugin gets it, otherwise it should be same as TrimmedQuery. /// Since we allow user to switch a exclusive plugin to generic plugin, /// so this property will always give you the "real" query part of the query /// @@ -103,6 +122,6 @@ namespace Flow.Launcher.Plugin } /// - public override string ToString() => RawQuery; + public override string ToString() => TrimmedQuery; } } diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index 2c9b8d4fd..9404d1107 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -4,11 +4,13 @@ using System.IO; using System.Threading.Tasks; using System.Windows.Controls; using System.Windows.Media; +using System.Text.Json.Serialization; namespace Flow.Launcher.Plugin { /// - /// Describes a result of a executed by a plugin + /// Describes a result of a executed by a plugin. + /// This or its child classes is serializable. /// public class Result { @@ -21,6 +23,8 @@ namespace Flow.Launcher.Plugin private string _icoPath; + private string _icoPathAbsolute; + private string _copyText = string.Empty; private string _badgeIcoPath; @@ -64,15 +68,27 @@ namespace Flow.Launcher.Plugin public string AutoCompleteText { get; set; } /// - /// The image to be displayed for the result. + /// Path or URI to the icon image for this result. + /// Updates appropriately when set. /// - /// Can be a local file path or a URL. - /// GlyphInfo is prioritized if not null + /// + /// Preferred usage: provide a path relative to the plugin directory (for example: "Images\icon.png"). + /// Because is serialized, using relative paths keeps the icon reference portable + /// when Flow is moved. + /// + /// Accepted formats: + /// - Relative file paths (resolved against into ) + /// - Absolute file paths (left as-is) + /// - HTTP/HTTPS URLs (left as-is) + /// - Data URIs (left as-is) + /// public string IcoPath { get => _icoPath; set { + _icoPath = value; + // As a standard this property will handle prepping and converting to absolute local path for icon image processing if (!string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(PluginDirectory) @@ -81,15 +97,23 @@ namespace Flow.Launcher.Plugin && !value.StartsWith("https://", StringComparison.OrdinalIgnoreCase) && !value.StartsWith("data:image", StringComparison.OrdinalIgnoreCase)) { - _icoPath = Path.Combine(PluginDirectory, value); + _icoPathAbsolute = Path.Combine(PluginDirectory, value); } else { - _icoPath = value; + _icoPathAbsolute = value; } } } + /// + /// Absolute path or URI which is used to load and display the result icon for Flow. + /// This is populated by the setter. + /// If a relative path was provided to , this property will contain the resolved + /// absolute local path after combining with . + /// + public string IcoPathAbsolute => _icoPathAbsolute; + /// /// The image to be displayed for the badge of the result. /// @@ -131,17 +155,34 @@ namespace Flow.Launcher.Plugin /// /// Delegate to load an icon for this result. /// + [JsonIgnore] public IconDelegate Icon = null; /// /// Delegate to load an icon for the badge of this result. /// + [JsonIgnore] public IconDelegate BadgeIcon = null; + private GlyphInfo _glyph; + /// /// Information for Glyph Icon (Prioritized than IcoPath/Icon if user enable Glyph Icons) /// - public GlyphInfo Glyph { get; init; } + public GlyphInfo Glyph + { + get => _glyph; + init => _glyph = value; + } + + /// + /// Set the Glyph Icon after initialization + /// + /// + public void SetGlyph(GlyphInfo glyph) + { + _glyph = glyph; + } /// /// An action to take in the form of a function call when the result has been selected. @@ -151,6 +192,7 @@ namespace Flow.Launcher.Plugin /// Its result determines what happens to Flow Launcher's query form: /// when true, the form will be hidden; when false, it will stay in focus. /// + [JsonIgnore] public Func Action { get; set; } /// @@ -161,6 +203,7 @@ namespace Flow.Launcher.Plugin /// Its result determines what happens to Flow Launcher's query form: /// when true, the form will be hidden; when false, it will stay in focus. /// + [JsonIgnore] public Func> AsyncAction { get; set; } /// @@ -203,11 +246,13 @@ namespace Flow.Launcher.Plugin /// /// As external information for ContextMenu /// + [JsonIgnore] public object ContextData { get; set; } /// /// Plugin ID that generated this result /// + [JsonInclude] public string PluginID { get; internal set; } /// @@ -223,6 +268,7 @@ namespace Flow.Launcher.Plugin /// /// Customized Preview Panel /// + [JsonIgnore] public Lazy PreviewPanel { get; set; } /// @@ -352,6 +398,7 @@ namespace Flow.Launcher.Plugin /// /// Delegate to get the preview panel's image /// + [JsonIgnore] public IconDelegate PreviewDelegate { get; set; } = null; /// diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs index 3af57f00d..cd1ddf983 100644 --- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs +++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs @@ -130,6 +130,119 @@ namespace Flow.Launcher.Plugin.SharedCommands } } + /// + /// Attempts to delete a directory robustly with retry logic for locked files. + /// This method tries to delete files individually with retries, then removes empty directories. + /// Returns true if the directory was completely deleted, false if some files/folders remain. + /// + /// The directory path to delete + /// Maximum number of retry attempts for locked files (default: 3) + /// Delay in milliseconds between retries (default: 100ms) + /// True if directory was fully deleted, false if some items remain + public static bool TryDeleteDirectoryRobust(string path, int maxRetries = 3, int retryDelayMs = 100) + { + if (!Directory.Exists(path)) + return true; + + bool fullyDeleted = true; + + try + { + // First, try to delete all files in the directory tree + var files = Directory.GetFiles(path, "*", SearchOption.AllDirectories); + foreach (var file in files) + { + bool fileDeleted = false; + for (int attempt = 0; attempt <= maxRetries; attempt++) + { + try + { + // Remove read-only attribute if present + var fileInfo = new FileInfo(file); + if (fileInfo.Exists && fileInfo.IsReadOnly) + { + fileInfo.IsReadOnly = false; + } + + File.Delete(file); + fileDeleted = true; + break; + } + catch (UnauthorizedAccessException) + { + // File is in use or access denied, wait and retry + if (attempt < maxRetries) + { + System.Threading.Thread.Sleep(retryDelayMs); + } + } + catch (IOException) + { + // File is in use, wait and retry + if (attempt < maxRetries) + { + System.Threading.Thread.Sleep(retryDelayMs); + } + } + catch + { + // Other exceptions, don't retry + break; + } + } + + if (!fileDeleted) + { + fullyDeleted = false; + } + } + + // Then, try to delete all empty directories (from deepest to shallowest) + var directories = Directory.GetDirectories(path, "*", SearchOption.AllDirectories) + .OrderByDescending(d => d.Length) // Delete deeper directories first + .ToArray(); + + foreach (var directory in directories) + { + try + { + if (Directory.Exists(directory) && !Directory.EnumerateFileSystemEntries(directory).Any()) + { + Directory.Delete(directory, false); + } + } + catch + { + // If we can't delete an empty directory, mark as not fully deleted + fullyDeleted = false; + } + } + + // Finally, try to delete the root directory itself + try + { + if (Directory.Exists(path) && !Directory.EnumerateFileSystemEntries(path).Any()) + { + Directory.Delete(path, false); + } + else if (Directory.Exists(path)) + { + fullyDeleted = false; + } + } + catch + { + fullyDeleted = false; + } + } + catch + { + fullyDeleted = false; + } + + return fullyDeleted; + } + /// /// Checks if a directory exists /// diff --git a/Flow.Launcher.Plugin/packages.lock.json b/Flow.Launcher.Plugin/packages.lock.json index 70f71f20d..7565ec3f4 100644 --- a/Flow.Launcher.Plugin/packages.lock.json +++ b/Flow.Launcher.Plugin/packages.lock.json @@ -16,23 +16,23 @@ }, "Microsoft.SourceLink.GitHub": { "type": "Direct", - "requested": "[8.0.0, )", - "resolved": "8.0.0", - "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "requested": "[10.0.103, )", + "resolved": "10.0.103", + "contentHash": "qZk7r40ftpZY+/sO019sgWAWfNqC2CLSspDdAxNYCJU/bCi/8jVwvOMjzb/d5gjCRNzQ4OCYgBfhdpQyVwLTyw==", "dependencies": { - "Microsoft.Build.Tasks.Git": "8.0.0", - "Microsoft.SourceLink.Common": "8.0.0" + "Microsoft.Build.Tasks.Git": "10.0.103", + "Microsoft.SourceLink.Common": "10.0.103" } }, "Microsoft.Windows.CsWin32": { "type": "Direct", - "requested": "[0.3.205, )", - "resolved": "0.3.205", - "contentHash": "U5wGAnyKd7/I2YMd43nogm81VMtjiKzZ9dsLMVI4eAB7jtv5IEj0gprj0q/F3iRmAIaGv5omOf8iSYx2+nE6BQ==", + "requested": "[0.3.269, )", + "resolved": "0.3.269", + "contentHash": "O4GVJ0ymxcoFRGS07VcoEClj7A9PIciHIjWDrPymzonhYlOfM7V0ZqGBUK19cUH3BPca9MfSOH0KLK/9JzQ8+Q==", "dependencies": { "Microsoft.Windows.SDK.Win32Docs": "0.1.42-alpha", - "Microsoft.Windows.SDK.Win32Metadata": "61.0.15-preview", - "Microsoft.Windows.WDK.Win32Metadata": "0.12.8-experimental" + "Microsoft.Windows.SDK.Win32Metadata": "69.0.7-preview", + "Microsoft.Windows.WDK.Win32Metadata": "0.13.25-experimental" } }, "PropertyChanged.Fody": { @@ -46,13 +46,13 @@ }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + "resolved": "10.0.103", + "contentHash": "QoiCMcPuxC6eqRQmrmF9zBY96ejIznXtve/lJJbonGD9I5Aygf2AUCOWslGiCEtBbfWRSuUnepBjuuVOdAl5ag==" }, "Microsoft.SourceLink.Common": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + "resolved": "10.0.103", + "contentHash": "cMtGW5/r0ck72Jg2QwZcNTX59z+iB/B1kW84VMa/eX8L19DhHIuIcQjfK0pgLLBxd60Jl0Bj9GUolcM0MnJnZA==" }, "Microsoft.Windows.SDK.Win32Docs": { "type": "Transitive", @@ -61,15 +61,15 @@ }, "Microsoft.Windows.SDK.Win32Metadata": { "type": "Transitive", - "resolved": "61.0.15-preview", - "contentHash": "cysex3dazKtCPALCluC2XX3f5Aedy9H2pw5jb+TW5uas2rkem1Z7FRnbUrg2vKx0pk0Qz+4EJNr37HdYTEcvEQ==" + "resolved": "69.0.7-preview", + "contentHash": "RJoNjQJVCIDNLPbvYuaygCFknTyAxOUE45of1voj0jjOgJa9MB2m1/G8L8F3IYc+2EFG5aqa/9y8PEx7Tk2tLQ==" }, "Microsoft.Windows.WDK.Win32Metadata": { "type": "Transitive", - "resolved": "0.12.8-experimental", - "contentHash": "3n8R44/Z96Ly+ty4eYVJfESqbzvpw96lRLs3zOzyDmr1x1Kw7FNn5CyE416q+bZQV3e1HRuMUvyegMeRE/WedA==", + "resolved": "0.13.25-experimental", + "contentHash": "IM50tb/+UIwBr9FMr6ZKcZjCMW+Axo6NjGqKxgjUfyCY8dRnYUfrJEXxAaXoWtYP4X8EmASmC1Jtwh4XucseZg==", "dependencies": { - "Microsoft.Windows.SDK.Win32Metadata": "61.0.15-preview" + "Microsoft.Windows.SDK.Win32Metadata": "63.0.31-preview" } } } diff --git a/Flow.Launcher.Test/FilesFoldersTest.cs b/Flow.Launcher.Test/FilesFoldersTest.cs index 2621fc2da..a63b59c39 100644 --- a/Flow.Launcher.Test/FilesFoldersTest.cs +++ b/Flow.Launcher.Test/FilesFoldersTest.cs @@ -1,6 +1,7 @@ using Flow.Launcher.Plugin.SharedCommands; using NUnit.Framework; using NUnit.Framework.Legacy; +using System.IO; namespace Flow.Launcher.Test { @@ -50,5 +51,89 @@ namespace Flow.Launcher.Test { ClassicAssert.AreEqual(expectedResult, FilesFolders.PathContains(parentPath, path, allowEqual: expectedResult)); } + + [Test] + public void TryDeleteDirectoryRobust_WhenDirectoryDoesNotExist_ReturnsTrue() + { + // Arrange + string nonExistentPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + + // Act + bool result = FilesFolders.TryDeleteDirectoryRobust(nonExistentPath); + + // Assert + ClassicAssert.IsTrue(result); + } + + [Test] + public void TryDeleteDirectoryRobust_WhenDirectoryIsEmpty_DeletesSuccessfully() + { + // Arrange + string tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(tempDir); + + // Act + bool result = FilesFolders.TryDeleteDirectoryRobust(tempDir); + + // Assert + ClassicAssert.IsTrue(result); + ClassicAssert.IsFalse(Directory.Exists(tempDir)); + } + + [Test] + public void TryDeleteDirectoryRobust_WhenDirectoryHasFiles_DeletesSuccessfully() + { + // Arrange + string tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(tempDir); + File.WriteAllText(Path.Combine(tempDir, "test.txt"), "test content"); + + // Act + bool result = FilesFolders.TryDeleteDirectoryRobust(tempDir); + + // Assert + ClassicAssert.IsTrue(result); + ClassicAssert.IsFalse(Directory.Exists(tempDir)); + } + + [Test] + public void TryDeleteDirectoryRobust_WhenDirectoryHasNestedStructure_DeletesSuccessfully() + { + // Arrange + string tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(tempDir); + string subDir1 = Path.Combine(tempDir, "SubDir1"); + string subDir2 = Path.Combine(tempDir, "SubDir2"); + Directory.CreateDirectory(subDir1); + Directory.CreateDirectory(subDir2); + File.WriteAllText(Path.Combine(subDir1, "file1.txt"), "content1"); + File.WriteAllText(Path.Combine(subDir2, "file2.txt"), "content2"); + File.WriteAllText(Path.Combine(tempDir, "root.txt"), "root content"); + + // Act + bool result = FilesFolders.TryDeleteDirectoryRobust(tempDir); + + // Assert + ClassicAssert.IsTrue(result); + ClassicAssert.IsFalse(Directory.Exists(tempDir)); + } + + [Test] + public void TryDeleteDirectoryRobust_WhenFileIsReadOnly_RemovesAttributeAndDeletes() + { + // Arrange + string tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(tempDir); + string filePath = Path.Combine(tempDir, "readonly.txt"); + File.WriteAllText(filePath, "readonly content"); + File.SetAttributes(filePath, FileAttributes.ReadOnly); + + // Act + bool result = FilesFolders.TryDeleteDirectoryRobust(tempDir); + + // Assert + ClassicAssert.IsTrue(result); + ClassicAssert.IsFalse(Directory.Exists(tempDir)); + } } } diff --git a/Flow.Launcher.Test/Plugins/CalculatorTest.cs b/Flow.Launcher.Test/Plugins/CalculatorTest.cs index b075813db..4e40d3645 100644 --- a/Flow.Launcher.Test/Plugins/CalculatorTest.cs +++ b/Flow.Launcher.Test/Plugins/CalculatorTest.cs @@ -16,14 +16,15 @@ namespace Flow.Launcher.Test.Plugins { DecimalSeparator = DecimalSeparator.UseSystemLocale, MaxDecimalPlaces = 10, - ShowErrorMessage = false // Make sure we return the empty results when error occurs + ShowErrorMessage = false, // Make sure we return the empty results when error occurs + UseThousandsSeparator = true // Default value }; private readonly Engine _engine = new(new Configuration { Scope = new Dictionary - { - { "e", Math.E }, // e is not contained in the default mages engine - } + { + { "e", Math.E }, // e is not contained in the default mages engine + } }); public CalculatorPluginTest() @@ -41,6 +42,44 @@ namespace Flow.Launcher.Test.Plugins engineField.SetValue(null, _engine); } + [Test] + public void ThousandsSeparatorTest_Enabled() + { + _settings.UseThousandsSeparator = true; + + _settings.DecimalSeparator = DecimalSeparator.Dot; + var result = GetCalculationResult("1000+234"); + // When thousands separator is enabled, the result should contain a separator + // Since decimal separator is dot, thousands separator should be comma + ClassicAssert.AreEqual("1,234", result); + + _settings.DecimalSeparator = DecimalSeparator.Comma; + var result2 = GetCalculationResult("1000+234"); + // When thousands separator is enabled, the result should contain a separator + // Since decimal separator is comma, thousands separator should be dot + ClassicAssert.AreEqual("1.234", result2); + } + + [Test] + public void ThousandsSeparatorTest_Disabled() + { + _settings.UseThousandsSeparator = false; + _settings.DecimalSeparator = DecimalSeparator.UseSystemLocale; + + var result = GetCalculationResult("1000+234"); + ClassicAssert.AreEqual("1234", result); + } + + [Test] + public void ThousandsSeparatorTest_LargeNumber() + { + _settings.UseThousandsSeparator = false; + _settings.DecimalSeparator = DecimalSeparator.UseSystemLocale; + + var result = GetCalculationResult("1000000+234567"); + ClassicAssert.AreEqual("1234567", result); + } + // Basic operations [TestCase(@"1+1", "2")] [TestCase(@"2-1", "1")] @@ -77,6 +116,9 @@ namespace Flow.Launcher.Test.Plugins [TestCase(@"invalid_expression", "")] public void CalculatorTest(string expression, string result) { + _settings.UseThousandsSeparator = false; + _settings.DecimalSeparator = DecimalSeparator.Dot; + ClassicAssert.AreEqual(GetCalculationResult(expression), result); } diff --git a/Flow.Launcher.Test/QueryBuilderTest.cs b/Flow.Launcher.Test/QueryBuilderTest.cs index c8ac17748..0ede781f8 100644 --- a/Flow.Launcher.Test/QueryBuilderTest.cs +++ b/Flow.Launcher.Test/QueryBuilderTest.cs @@ -16,9 +16,9 @@ namespace Flow.Launcher.Test {">", new PluginPair {Metadata = new PluginMetadata {ActionKeywords = new List {">"}}}} }; - Query q = QueryBuilder.Build("> ping google.com -n 20 -6", nonGlobalPlugins); + Query q = QueryBuilder.Build("> ping google.com -n 20 -6", "> ping google.com -n 20 -6", nonGlobalPlugins); - ClassicAssert.AreEqual("> ping google.com -n 20 -6", q.RawQuery); + ClassicAssert.AreEqual("> ping google.com -n 20 -6", q.TrimmedQuery); ClassicAssert.AreEqual("ping google.com -n 20 -6", q.Search, "Search should not start with the ActionKeyword."); ClassicAssert.AreEqual(">", q.ActionKeyword); @@ -39,10 +39,10 @@ namespace Flow.Launcher.Test {">", new PluginPair {Metadata = new PluginMetadata {ActionKeywords = new List {">"}, Disabled = true}}} }; - Query q = QueryBuilder.Build("> ping google.com -n 20 -6", nonGlobalPlugins); + Query q = QueryBuilder.Build("> ping google.com -n 20 -6", "> ping google.com -n 20 -6", nonGlobalPlugins); ClassicAssert.AreEqual("> ping google.com -n 20 -6", q.Search); - ClassicAssert.AreEqual(q.Search, q.RawQuery, "RawQuery should be equal to Search."); + ClassicAssert.AreEqual(q.Search, q.TrimmedQuery, "TrimmedQuery should be equal to Search."); ClassicAssert.AreEqual(6, q.SearchTerms.Length, "The length of SearchTerms should match."); ClassicAssert.AreNotEqual(">", q.ActionKeyword, "ActionKeyword should not match that of a disabled plugin."); ClassicAssert.AreEqual("ping google.com -n 20 -6", q.SecondToEndSearch, "SecondToEndSearch should be trimmed of multiple whitespace characters"); @@ -51,7 +51,7 @@ namespace Flow.Launcher.Test [Test] public void GenericPluginQueryTest() { - Query q = QueryBuilder.Build("file.txt file2 file3", new Dictionary()); + Query q = QueryBuilder.Build("file.txt file2 file3", "file.txt file2 file3", new Dictionary()); ClassicAssert.AreEqual("file.txt file2 file3", q.Search); ClassicAssert.AreEqual("", q.ActionKeyword); diff --git a/Flow.Launcher.Test/TranslationMappingTest.cs b/Flow.Launcher.Test/TranslationMappingTest.cs index bd3636f0a..a3c0026c0 100644 --- a/Flow.Launcher.Test/TranslationMappingTest.cs +++ b/Flow.Launcher.Test/TranslationMappingTest.cs @@ -22,19 +22,33 @@ namespace Flow.Launcher.Test ClassicAssert.AreEqual(10, GetOriginalToTranslatedAt(mapping, 1)); } - [TestCase(0, 0)] - [TestCase(2, 1)] - [TestCase(3, 1)] - [TestCase(5, 2)] - [TestCase(6, 2)] + + [TestCase(0, 0)] // "F" -> "F" + [TestCase(1, 1)] // "l" -> "l" + [TestCase(2, 2)] // "o" -> "o" + [TestCase(3, 3)] // "w" -> "w" + [TestCase(4, 4)] // " " -> " " + [TestCase(5, 5)] // "Y" (translated from "用") -> original index 5 + [TestCase(6, 5)] // "o" (translated from "用") -> original index 5 + [TestCase(7, 5)] // "n" (translated from "用") -> original index 5 + [TestCase(8, 5)] // "g" (translated from "用") -> original index 5 + [TestCase(10, 6)] // "H" (translated from "户") -> original index 6 + [TestCase(11, 6)] // "u" (translated from "户") -> original index 6 public void MapToOriginalIndex_ShouldReturnExpectedIndex(int translatedIndex, int expectedOriginalIndex) { var mapping = new TranslationMapping(); - // a测试 - // a Ce Shi - mapping.AddNewIndex(0, 1); - mapping.AddNewIndex(2, 2); - mapping.AddNewIndex(5, 3); + // Test case : + // 0123456 + // Flow 用户 + // 012345678901 + // Flow Yong Hu + mapping.AddNewIndex(0, 1); // F + mapping.AddNewIndex(1, 1); // l + mapping.AddNewIndex(2, 1); // o + mapping.AddNewIndex(3, 1); // w + mapping.AddNewIndex(4, 1); // ' ' + mapping.AddNewIndex(5, 4); // 用 -> Yong + mapping.AddNewIndex(10, 2); // 户 -> Hu var result = mapping.MapToOriginalIndex(translatedIndex); ClassicAssert.AreEqual(expectedOriginalIndex, result); diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 7443d7b5b..3eee195a7 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -268,6 +268,9 @@ namespace Flow.Launcher await PluginManager.InitializePluginsAsync(_mainVM); + // Refresh the history results after plugins are initialized so that we can parse the absolute icon paths + _mainVM.RefreshLastOpenedHistoryResults(); + // Refresh home page after plugins are initialized because users may open main window during plugin initialization // And home page is created without full plugin list if (_settings.ShowHomePage && _mainVM.QueryResultsSelected() && string.IsNullOrEmpty(_mainVM.QueryText)) diff --git a/Flow.Launcher/Helper/AutoStartup.cs b/Flow.Launcher/Helper/AutoStartup.cs index a33c55135..59740d048 100644 --- a/Flow.Launcher/Helper/AutoStartup.cs +++ b/Flow.Launcher/Helper/AutoStartup.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Linq; using System.Security.Principal; using Flow.Launcher.Infrastructure; @@ -61,14 +62,15 @@ public class AutoStartup { if (task.Definition.Actions.FirstOrDefault() is Microsoft.Win32.TaskScheduler.Action taskAction) { - var action = taskAction.ToString().Trim(); + var action = taskAction.ToString().Trim(); var pathCorrect = action.Equals(Constant.ExecutablePath, StringComparison.OrdinalIgnoreCase); var runLevelCorrect = CheckRunLevel(task.Definition.Principal.RunLevel, alwaysRunAsAdministrator); + var priorityCorrect = task.Definition.Settings.Priority == ProcessPriorityClass.Normal; if (Win32Helper.IsAdministrator()) { - // If path or run level is not correct, we need to unschedule and reschedule the task - if (!pathCorrect || !runLevelCorrect) + // If path, run level or priority is not correct, we need to unschedule and reschedule the task + if (!pathCorrect || !runLevelCorrect || !priorityCorrect) { UnscheduleLogonTask(); ScheduleLogonTask(alwaysRunAsAdministrator); @@ -83,8 +85,8 @@ public class AutoStartup throw new UnauthorizedAccessException("Cannot edit task run level because the app is not running as administrator."); } - // If run level is correct and path is not correct, we need to unschedule and reschedule the task - if (!pathCorrect) + // If run level is correct and path or priority is not correct, we need to unschedule and reschedule the task + if (!pathCorrect || !priorityCorrect) { UnscheduleLogonTask(); ScheduleLogonTask(alwaysRunAsAdministrator); @@ -219,6 +221,7 @@ public class AutoStartup td.Settings.StopIfGoingOnBatteries = false; td.Settings.DisallowStartIfOnBatteries = false; td.Settings.ExecutionTimeLimit = TimeSpan.Zero; + td.Settings.Priority = ProcessPriorityClass.Normal; try { diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index bb1cddc6c..0a2826484 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -143,6 +143,8 @@ internal static class HotKeyMapper return; App.API.ShowMainWindow(); + // Make sure to go back to the query results page first since it can cause issues if current page is context menu + App.API.BackToQueryResults(); App.API.ChangeQuery(hotkey.ActionKeyword, true); }); } diff --git a/Flow.Launcher/Helper/ResultHelper.cs b/Flow.Launcher/Helper/ResultHelper.cs index 5f9a69f28..017651fdf 100644 --- a/Flow.Launcher/Helper/ResultHelper.cs +++ b/Flow.Launcher/Helper/ResultHelper.cs @@ -11,20 +11,20 @@ namespace Flow.Launcher.Helper; public static class ResultHelper { - public static async Task PopulateResultsAsync(LastOpenedHistoryItem item) + public static async Task PopulateResultsAsync(LastOpenedHistoryResult item) { return await PopulateResultsAsync(item.PluginID, item.Query, item.Title, item.SubTitle, item.RecordKey); } - public static async Task PopulateResultsAsync(string pluginId, string rawQuery, string title, string subTitle, string recordKey) + public static async Task PopulateResultsAsync(string pluginId, string trimmedQuery, string title, string subTitle, string recordKey) { var plugin = PluginManager.GetPluginForId(pluginId); if (plugin == null) return null; - var query = QueryBuilder.Build(rawQuery, PluginManager.GetNonGlobalPlugins()); + var query = QueryBuilder.Build(trimmedQuery, trimmedQuery, PluginManager.GetNonGlobalPlugins()); if (query == null) return null; try { - var freshResults = await plugin.Plugin.QueryAsync(query, CancellationToken.None); + var freshResults = await PluginManager.QueryForPluginAsync(plugin, query, CancellationToken.None); // Try to match by record key first if it is valid, otherwise fall back to title + subtitle match if (string.IsNullOrEmpty(recordKey)) { diff --git a/Flow.Launcher/Languages/ar.xaml b/Flow.Launcher/Languages/ar.xaml index 67f1f766e..b1c9ca426 100644 --- a/Flow.Launcher/Languages/ar.xaml +++ b/Flow.Launcher/Languages/ar.xaml @@ -79,6 +79,8 @@ After uninstallation, you need to manually remove this task (Flow.Launcher Startup) via Task Scheduler خطأ في إعداد التشغيل عند بدء التشغيل إخفاء Flow Launcher عند فقدان التركيز + Show taskbar when Flow Launcher is opened + Temporarily show the taskbar when Flow Launcher is opened, useful for auto-hidden taskbars. عدم عرض إشعارات الإصدار الجديد Search Window Location تذكر آخر موقع diff --git a/Flow.Launcher/Languages/cs.xaml b/Flow.Launcher/Languages/cs.xaml index 96cbe95e7..b55026e28 100644 --- a/Flow.Launcher/Languages/cs.xaml +++ b/Flow.Launcher/Languages/cs.xaml @@ -79,6 +79,8 @@ After uninstallation, you need to manually remove this task (Flow.Launcher Startup) via Task Scheduler Při nastavování spouštění došlo k chybě Skrýt Flow Launcher při vykliknutí + Show taskbar when Flow Launcher is opened + Temporarily show the taskbar when Flow Launcher is opened, useful for auto-hidden taskbars. Nezobrazovat oznámení o nové verzi Search Window Location Zapamatovat poslední pozici diff --git a/Flow.Launcher/Languages/da.xaml b/Flow.Launcher/Languages/da.xaml index a1fc771d2..7d4094c84 100644 --- a/Flow.Launcher/Languages/da.xaml +++ b/Flow.Launcher/Languages/da.xaml @@ -79,6 +79,8 @@ After uninstallation, you need to manually remove this task (Flow.Launcher Startup) via Task Scheduler Error setting launch on startup Skjul Flow Launcher ved mistet fokus + Show taskbar when Flow Launcher is opened + Temporarily show the taskbar when Flow Launcher is opened, useful for auto-hidden taskbars. Vis ikke notifikationer om nye versioner Search Window Location Remember Last Position diff --git a/Flow.Launcher/Languages/de.xaml b/Flow.Launcher/Languages/de.xaml index 2c083646f..32f8d5d2b 100644 --- a/Flow.Launcher/Languages/de.xaml +++ b/Flow.Launcher/Languages/de.xaml @@ -79,6 +79,8 @@ Nach der Deinstallation müssen Sie diese Aufgabe (Flow.Launcher Startup) via Task-Scheduler manuell entfernen Fehler bei Einstellungsstart beim Start Flow Launcher ausblenden, wenn Fokus verloren geht + Show taskbar when Flow Launcher is opened + Temporarily show the taskbar when Flow Launcher is opened, useful for auto-hidden taskbars. Versionsbenachrichtigungen nicht zeigen Ort des Suchfensters Letzte Position merken diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index df1709e81..b5daa0bfa 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -82,6 +82,8 @@ After uninstallation, you need to manually remove this task (Flow.Launcher Startup) via Task Scheduler Error setting launch on startup Hide Flow Launcher when focus is lost + Show taskbar when Flow Launcher is opened + Temporarily show the taskbar when Flow Launcher is opened, useful for auto-hidden taskbars. Do not show new version notifications Search Window Location Remember Last Position diff --git a/Flow.Launcher/Languages/es-419.xaml b/Flow.Launcher/Languages/es-419.xaml index 9f06c4436..40e8cb978 100644 --- a/Flow.Launcher/Languages/es-419.xaml +++ b/Flow.Launcher/Languages/es-419.xaml @@ -79,6 +79,8 @@ After uninstallation, you need to manually remove this task (Flow.Launcher Startup) via Task Scheduler Error setting launch on startup Ocultar Flow Launcher cuando se pierde el enfoque + Show taskbar when Flow Launcher is opened + Temporarily show the taskbar when Flow Launcher is opened, useful for auto-hidden taskbars. No mostrar notificaciones de nuevas versiones Search Window Location Remember Last Position diff --git a/Flow.Launcher/Languages/es.xaml b/Flow.Launcher/Languages/es.xaml index f7a3fbb22..049b33858 100644 --- a/Flow.Launcher/Languages/es.xaml +++ b/Flow.Launcher/Languages/es.xaml @@ -79,6 +79,8 @@ Después de la desinstalación, es necesario eliminar manualmente la tarea (Flow.Launcher Startup) mediante el Programador de Tareas Error de configuración de arranque al iniciar Ocultar Flow Launcher cuando se pierde el foco + Show taskbar when Flow Launcher is opened + Temporarily show the taskbar when Flow Launcher is opened, useful for auto-hidden taskbars. No mostrar notificaciones de nuevas versiones Ubicación de la ventana de búsqueda Recordar última ubicación diff --git a/Flow.Launcher/Languages/fr.xaml b/Flow.Launcher/Languages/fr.xaml index bdce6ddb9..d60d53a1c 100644 --- a/Flow.Launcher/Languages/fr.xaml +++ b/Flow.Launcher/Languages/fr.xaml @@ -79,6 +79,8 @@ Après une désinstallation, vous devez supprimer manuellement cette tâche (Flow.Launcher Startup) via le planificateur de tâches Erreur lors de la configuration du lancement au démarrage Cacher Flow Launcher lors de la perte de focus + Afficher la barre des tâches lorsque Flow Launcher est ouvert + Afficher temporairement la barre des tâches lorsque Flow Launcher est ouvert, utile pour les barres de tâches auto-masquées. Ne pas afficher le message de mise à jour pour les nouvelles versions Emplacement de la fenêtre de recherche Se souvenir de la dernière position diff --git a/Flow.Launcher/Languages/he.xaml b/Flow.Launcher/Languages/he.xaml index 39f02c702..926d94886 100644 --- a/Flow.Launcher/Languages/he.xaml +++ b/Flow.Launcher/Languages/he.xaml @@ -8,9 +8,9 @@ אנא בחר את קובץ ההפעלה {0} - Your selected {0} executable is invalid. + קובץ ההפעלה {0} שבחרת אינו חוקי. {2}{2} - Click yes if you would like select the {0} executable again. Click no if you would like to download {1} + לחץ על כן אם ברצונך, בחר את {0} ההפעלה הקודמת. לחץ על לא אם ברצונך להוריד את {1} לא ניתן להגדיר נתיב הפעלה {0}, אנא נסה שוב בהגדרות Flow (גלול עד למטה). נכשל בהפעלת תוספים @@ -79,6 +79,8 @@ לאחר הסרת ההתקנה, עליך להסיר ידנית משימה זו (Flow.Launcher Startup) דרך מתזמן המשימות שגיאה בהגדרת ההפעלה בעת הפעלת windows הסתר את Flow Launcher כאשר הוא אינו החלון הפעיל + Show taskbar when Flow Launcher is opened + Temporarily show the taskbar when Flow Launcher is opened, useful for auto-hidden taskbars. אל תציג התראות על גרסה חדשה מיקום חלון חיפוש זכור את המיקום האחרון diff --git a/Flow.Launcher/Languages/it.xaml b/Flow.Launcher/Languages/it.xaml index ffb716658..92afa5436 100644 --- a/Flow.Launcher/Languages/it.xaml +++ b/Flow.Launcher/Languages/it.xaml @@ -79,6 +79,8 @@ After uninstallation, you need to manually remove this task (Flow.Launcher Startup) via Task Scheduler Errore nell'impostazione del lancio all'avvio Nascondi Flow Launcher quando perde il focus + Show taskbar when Flow Launcher is opened + Temporarily show the taskbar when Flow Launcher is opened, useful for auto-hidden taskbars. Non mostrare le notifiche per una nuova versione Search Window Location Ricorda L'Ultima Posizione @@ -147,8 +149,8 @@ Search Delay Adds a short delay while typing to reduce UI flicker and result load. Recommended if your typing speed is average. Enter the wait time (in ms) until input is considered complete. This can only be edited if Search Delay is enabled. - Default Search Delay Time - Wait time before showing results after typing stops. Higher values wait longer. (ms) + Tempo predefinito ritardo ricerca + Tempo di attesa prima di mostrare i risultati dopo l'interruzione della digitazione. Valori più alti attendono più a lungo. (ms) Information for Korean IME user The Korean input method used in Windows 11 may cause some issues in Flow Launcher. @@ -265,11 +267,11 @@ Plugin update {0} di {1} {2}{2}Vuoi aggiornare questo plugin? Download del plugin - Automatically restart after installing/uninstalling/updating plugins in plugin store - Zip file does not have a valid plugin.json configuration + Riavvia automaticamente dopo l'installazione/disinstallazione/aggiornamento dei plugin nel Plugin Store + Il file zip non contiene una configurazione plugin.json valida Installazione da una fonte sconosciuta This plugin is from an unknown source and it may contain potential risks!{0}{0}Please ensure you understand where this plugin is from and that it is safe.{0}{0}Would you like to continue still?{0}{0}(You can switch off this warning in general section of setting window) - Zip files + File zip Please select zip file Install plugin from local path Nessun aggiornamento disponibile @@ -469,7 +471,7 @@ Cancella i log Sei sicuro di voler cancellare tutti i log? Cache Folder - Clear Caches + Cancella cache Are you sure you want to delete all caches? Failed to clear part of folders and files. Please see log file for more information Wizard @@ -636,7 +638,7 @@ Se si aggiunge un prefisso '@' mentre si inserisce una scorciatoia, corrisponde Restart Flow Launcher after updating plugins {0}: Update from v{1} to v{2} - No plugin selected + Nessun plugin selezionato Salta diff --git a/Flow.Launcher/Languages/ja.xaml b/Flow.Launcher/Languages/ja.xaml index 51ec99ddd..39b23f269 100644 --- a/Flow.Launcher/Languages/ja.xaml +++ b/Flow.Launcher/Languages/ja.xaml @@ -64,10 +64,10 @@ 位置のリセット 検索ウィンドウの位置をリセット ここに入力して検索 - {0}: This plugin is still initializing... - Select this result to requery - {0}: Failed to respond! - Select this result for more info + {0}: このプラグインはまだ初期化中です… + この結果を選択して再検索する + {0}: 応答に失敗しました! + 詳細については、この結果を選択してください 設定 @@ -79,6 +79,8 @@ アンインストール後は、「タスク スケジューラ」からこのタスク(Flow.Launcher Startup)を手動で削除する必要があります。 スタートアップ時に起動の設定失敗 フォーカスを失った時にFlow Launcherを隠す + Flow Launcher を開いたときにタスクバーを表示する + Flow Launcher を開いたときに一時的にタスクバーを表示します。タスクバーの自動非表示を設定している場合に便利です。 最新版が入手可能であっても、アップグレードメッセージを表示しない 検索ウィンドウの位置 最後の表示位置を記憶する @@ -169,7 +171,7 @@ 開く 前の韓国語IMEを使用 You can change the Previous Korean IME settings directly from here - Failed to change Korean IME setting + 韓国語IME設定の変更に失敗しました システムのレジストリへのアクセスが可能か確認するか、サポートにお問い合わせください。 ホームページ 検索文字列が空の場合、ホームページの結果を表示します。 @@ -454,7 +456,7 @@ アイコン あなたはFlow Launcherを {0} 回利用しました アップデートを確認する - Become a Sponsor + スポンサーになる 新しいバージョン {0} が利用可能です。Flow Launcherを再起動してください。 アップデートの確認に失敗しました、api.github.com への接続とプロキシ設定を確認してください。 diff --git a/Flow.Launcher/Languages/ko.xaml b/Flow.Launcher/Languages/ko.xaml index 503ff2f11..22f3bb011 100644 --- a/Flow.Launcher/Languages/ko.xaml +++ b/Flow.Launcher/Languages/ko.xaml @@ -79,6 +79,8 @@ Flow Launcher를 제거한 후에는 작업 스케줄러에서 이 작업(Flow.Launcher Startup)을 수동으로 삭제해야 합니다 Error setting launch on startup 포커스 잃으면 Flow Launcher 숨김 + Show taskbar when Flow Launcher is opened + Temporarily show the taskbar when Flow Launcher is opened, useful for auto-hidden taskbars. 새 버전 알림 끄기 검색 창 위치 마지막 위치 기억 diff --git a/Flow.Launcher/Languages/nb.xaml b/Flow.Launcher/Languages/nb.xaml index 8bd7f94a4..1ef057125 100644 --- a/Flow.Launcher/Languages/nb.xaml +++ b/Flow.Launcher/Languages/nb.xaml @@ -79,6 +79,8 @@ After uninstallation, you need to manually remove this task (Flow.Launcher Startup) via Task Scheduler Feil ved å sette kjør ved oppstart Skjul Flow Launcher når fokus forsvinner + Show taskbar when Flow Launcher is opened + Temporarily show the taskbar when Flow Launcher is opened, useful for auto-hidden taskbars. Ikke vis varsler om nye versjoner Search Window Location Husk siste posisjon diff --git a/Flow.Launcher/Languages/nl.xaml b/Flow.Launcher/Languages/nl.xaml index 8b7b86329..412781b55 100644 --- a/Flow.Launcher/Languages/nl.xaml +++ b/Flow.Launcher/Languages/nl.xaml @@ -79,6 +79,8 @@ After uninstallation, you need to manually remove this task (Flow.Launcher Startup) via Task Scheduler Fout bij het instellen van uitvoeren bij opstarten Verberg Flow Launcher als focus verloren is + Show taskbar when Flow Launcher is opened + Temporarily show the taskbar when Flow Launcher is opened, useful for auto-hidden taskbars. Laat geen nieuwe versie notificaties zien Search Window Location Laatste Positie Onthouden diff --git a/Flow.Launcher/Languages/pl.xaml b/Flow.Launcher/Languages/pl.xaml index 382626eb7..efc2ade55 100644 --- a/Flow.Launcher/Languages/pl.xaml +++ b/Flow.Launcher/Languages/pl.xaml @@ -79,6 +79,8 @@ Kliknij "nie", jeśli jest już zainstalowany. Zostaniesz wtedy popros Po odinstalowaniu musisz ręcznie usunąć to zadanie (Flow.Launcher Startup) za pomocą Harmonogramu zadań Błąd uruchamiania ustawień przy starcie Ukryj okno Flow Launcher kiedy przestanie ono być aktywne + Show taskbar when Flow Launcher is opened + Temporarily show the taskbar when Flow Launcher is opened, useful for auto-hidden taskbars. Nie pokazuj powiadomienia o nowej wersji Pozycja okna wyszukiwania Zapamiętaj Ostatnią Pozycję diff --git a/Flow.Launcher/Languages/pt-br.xaml b/Flow.Launcher/Languages/pt-br.xaml index 15dcae2f4..a7cf5ac68 100644 --- a/Flow.Launcher/Languages/pt-br.xaml +++ b/Flow.Launcher/Languages/pt-br.xaml @@ -79,6 +79,8 @@ After uninstallation, you need to manually remove this task (Flow.Launcher Startup) via Task Scheduler Erro ao ativar início com o sistema Esconder Flow Launcher quando foco for perdido + Show taskbar when Flow Launcher is opened + Temporarily show the taskbar when Flow Launcher is opened, useful for auto-hidden taskbars. Não mostrar notificações de novas versões Search Window Location Lembrar Última Posição diff --git a/Flow.Launcher/Languages/pt-pt.xaml b/Flow.Launcher/Languages/pt-pt.xaml index 3746ada13..a2b58b1a0 100644 --- a/Flow.Launcher/Languages/pt-pt.xaml +++ b/Flow.Launcher/Languages/pt-pt.xaml @@ -79,6 +79,8 @@ Se desinstalar a aplicação, tem que remover manualmente a tarefa (Flow.Launcher Startup) no agendamento de tarefas Erro ao definir para iniciar ao arrancar Ocultar Flow Launcher ao perder o foco + Mostrar barra de tarefas ao abrir Flow Launcher + Mostrar, temporariamente, a barra de tarefas ao abrir Flow launcher. Útil para barra de tarefas oculta automaticamente. Não notificar acerca de novas versões Posição da janela de pesquisa Memorizar última posição @@ -420,13 +422,13 @@ Default search window position. Displayed when triggered by search window hotkey Dialog Jump Result Navigation Behaviour Behaviour to navigate Open/Save As dialog window to the selected result path - Left click or Enter key - Right click + Clique esquerdo ou tecla Enter + Clique direito Dialog Jump File Navigation Behaviour Behaviour to navigate Open/Save As dialog window when the result is a file path - Fill full path in file name box - Fill full path in file name box and open - Fill directory in path box + Preencher caminho total na caixa Nome do ficheiro + Preencher caminho total na caixa Nome do ficheiro e abrir + Preencher diretório na caixa Caminho Proxy HTTP diff --git a/Flow.Launcher/Languages/ru.xaml b/Flow.Launcher/Languages/ru.xaml index 7bb5a8ff1..544816684 100644 --- a/Flow.Launcher/Languages/ru.xaml +++ b/Flow.Launcher/Languages/ru.xaml @@ -79,6 +79,8 @@ After uninstallation, you need to manually remove this task (Flow.Launcher Startup) via Task Scheduler Ошибка настройки запуска при запуске Скрывать Flow Launcher, если потерян фокуc + Show taskbar when Flow Launcher is opened + Temporarily show the taskbar when Flow Launcher is opened, useful for auto-hidden taskbars. Не отображать сообщение об обновлении, когда доступна новая версия Search Window Location Запомнить последнее положение diff --git a/Flow.Launcher/Languages/sk.xaml b/Flow.Launcher/Languages/sk.xaml index e76061a3b..c47b47ae1 100644 --- a/Flow.Launcher/Languages/sk.xaml +++ b/Flow.Launcher/Languages/sk.xaml @@ -80,6 +80,8 @@ Nevykonali sa žiadne zmeny. Po odinštalovaní musíte úlohu manuálne odstrániť (Flow.Launcher Startup) cez Plánovač úloh Chybné nastavenie spustenia pri spustení Schovať Flow Launcher po strate fokusu + Zobraziť panel úloh, keď je Flow Launcher otvorený + Dočasne zobraziť panel úloh pri otvorení Flow Launchera, užitočné pri automatickom skrývaní panela úloh. Nezobrazovať upozornenia na novú verziu Poloha vyhľadávacieho okna Zapamätať si poslednú pozíciu @@ -636,7 +638,7 @@ Ak pri zadávaní skratky pred ňu pridáte "@", bude sa zhodovať s Po aktualizácii pluginov reštartovať Flow Launcher - {0}: Aktualizované z v{1} na v{2} + {0}: Aktualizácia z v{1} na v{2} Nie je vybraný žiaden plugin diff --git a/Flow.Launcher/Languages/sr-Cyrl-RS.xaml b/Flow.Launcher/Languages/sr-Cyrl-RS.xaml index d7d60e6a0..824b48dbf 100644 --- a/Flow.Launcher/Languages/sr-Cyrl-RS.xaml +++ b/Flow.Launcher/Languages/sr-Cyrl-RS.xaml @@ -79,6 +79,8 @@ After uninstallation, you need to manually remove this task (Flow.Launcher Startup) via Task Scheduler Error setting launch on startup Hide Flow Launcher when focus is lost + Show taskbar when Flow Launcher is opened + Temporarily show the taskbar when Flow Launcher is opened, useful for auto-hidden taskbars. Do not show new version notifications Search Window Location Remember Last Position diff --git a/Flow.Launcher/Languages/sr.xaml b/Flow.Launcher/Languages/sr.xaml index 7d1bcb9f3..3ee982819 100644 --- a/Flow.Launcher/Languages/sr.xaml +++ b/Flow.Launcher/Languages/sr.xaml @@ -79,6 +79,8 @@ After uninstallation, you need to manually remove this task (Flow.Launcher Startup) via Task Scheduler Error setting launch on startup Sakri Flow Launcher kada se izgubi fokus + Show taskbar when Flow Launcher is opened + Temporarily show the taskbar when Flow Launcher is opened, useful for auto-hidden taskbars. Ne prikazuj obaveštenje o novoj verziji Search Window Location Remember Last Position diff --git a/Flow.Launcher/Languages/tr.xaml b/Flow.Launcher/Languages/tr.xaml index 67cdf9da4..023eb2aa6 100644 --- a/Flow.Launcher/Languages/tr.xaml +++ b/Flow.Launcher/Languages/tr.xaml @@ -79,6 +79,8 @@ Kaldırma işleminden sonra, bu görevi (Flow.Launcher Startup) Görev Zamanlayıcı üzerinden elle kaldırmanız gerekmektedir Sistemle başlatma ayarı başarısız oldu Odak Pencereden Ayrıldığında Gizle + Flow Launcher açıldığında görev çubuğunu göster + Flow Launcher açıldığında geçici olarak görev çubuğunu gösterir, otomatik gizlenen görev çubukları için kullanışlıdır. Güncelleme bildirimlerini gösterme Pencere Konumu Son Konumu Hatırla @@ -454,7 +456,7 @@ Kullanılan Simgeler Şu ana kadar Flow Launcher'ı {0} kez aktifleştirdiniz. Güncellemeleri Kontrol Et - Become a Sponsor + Sponsor Olun Uygulamanın yeni sürümü ({0}) mevcut, Lütfen Flow Launcher'ı yeniden başlatın. Güncelleme kontrolü başarısız oldu. Lütfen bağlantınız ve vekil sunucu ayarlarınızın api.github.com adresine ulaşabilir olduğunu kontrol edin. diff --git a/Flow.Launcher/Languages/uk-UA.xaml b/Flow.Launcher/Languages/uk-UA.xaml index bec1f85e3..0da6c7a47 100644 --- a/Flow.Launcher/Languages/uk-UA.xaml +++ b/Flow.Launcher/Languages/uk-UA.xaml @@ -79,6 +79,8 @@ Після видалення, вам необхідно вручну видалити це завдання (Flow.Launcher Startup) через планувальник завдань Помилка запуску налаштування під час запуску Сховати Flow Launcher, якщо втрачено фокус + Показувати панель завдань, коли Flow Launcher відкрито + Тимчасово показувати панель завдань при відкритті Flow Launcher, корисно для автоматично прихованих панелей завдань. Не повідомляти про доступні нові версії Розташування вікна пошуку Пам'ятати останню позицію diff --git a/Flow.Launcher/Languages/vi.xaml b/Flow.Launcher/Languages/vi.xaml index 5809c7837..463c11d0b 100644 --- a/Flow.Launcher/Languages/vi.xaml +++ b/Flow.Launcher/Languages/vi.xaml @@ -79,6 +79,8 @@ After uninstallation, you need to manually remove this task (Flow.Launcher Startup) via Task Scheduler Không lưu được tính năng tự khởi động khi khởi động hệ thống Ẩn Flow Launcher khi mất tiêu điểm + Show taskbar when Flow Launcher is opened + Temporarily show the taskbar when Flow Launcher is opened, useful for auto-hidden taskbars. Không hiển thị thông báo khi có phiên bản mới Search Window Location Ghi nhớ vị trí cuối cùng diff --git a/Flow.Launcher/Languages/zh-cn.xaml b/Flow.Launcher/Languages/zh-cn.xaml index 7af54dc54..089ba2dd1 100644 --- a/Flow.Launcher/Languages/zh-cn.xaml +++ b/Flow.Launcher/Languages/zh-cn.xaml @@ -2,7 +2,7 @@ - Flow 检测到您已安装 {0} 个插件,需要 {1} 才能运行。是否要下载 {1}? + Flow 检测到您已安装 {0} 插件,需要 {1} 才能运行。是否要下载 {1}? {2}{2} 如果已安装,请单击“否”,系统将提示您选择包含 {1} 可执行文件的文件夹 @@ -79,6 +79,8 @@ 卸载后,您需要通过任务计划程序手动移除此任务 (Flow.Launcher Startup) 设置开机自启时出错 失去焦点时自动隐藏 Flow Launcher + 打开 Flow 时显示任务栏 + 打开 Flow 时临时显示任务栏,用于自动隐藏任务栏 不显示新版本提示 搜索窗口位置 记住上次的位置 diff --git a/Flow.Launcher/Languages/zh-tw.xaml b/Flow.Launcher/Languages/zh-tw.xaml index ca7da7624..7aacc2190 100644 --- a/Flow.Launcher/Languages/zh-tw.xaml +++ b/Flow.Launcher/Languages/zh-tw.xaml @@ -2,43 +2,43 @@ - Flow detected you have installed {0} plugins, which will require {1} to run. Would you like to download {1}? + Flow 檢測到你已安裝 {0} 個插件,需要 {1} 才能運行。是否要下載 {1}? {2}{2} - Click no if it's already installed, and you will be prompted to select the folder that contains the {1} executable + 如果已安裝,請點擊“否”,系統將提示你選擇包含 {1} 個程式的資料夾 - Please select the {0} executable + 請選擇 {0} 可執行檔 - Your selected {0} executable is invalid. + 您所選擇的 {0} 可執行檔無效。 {2}{2} - Click yes if you would like select the {0} executable again. Click no if you would like to download {1} + 若要重新選取 {0} 可執行檔,請按「是」。若要下載 {1},請按「否」。 - Unable to set {0} executable path, please try from Flow's settings (scroll down to the bottom). - Fail to Init Plugins - Plugins: {0} - fail to load and would be disabled, please contact plugin creator for help + 無法設定 {0} 可執行檔路徑,請從 Flow 的設定中嘗試(向下捲動至最底部)。 + 初始化外掛失敗 + 外掛:{0} — 載入失敗,將被停用,請聯絡外掛開發者以取得協助 - Flow Launcher needs to restart to finish disabling portable mode, after the restart your portable data profile will be deleted and roaming data profile kept - Flow Launcher needs to restart to finish enabling portable mode, after the restart your roaming data profile will be deleted and portable data profile kept - Flow Launcher has detected you enabled portable mode, would you like to move it to a different location? - Flow Launcher has detected you disabled portable mode, the relevant shortcuts and uninstaller entry have been created - Flow Launcher detected your user data exists both in {0} and {1}. {2}{2}Please delete {1} in order to proceed. No changes have occurred. + Flow Launcher 需要重新啟動以完成停用可攜式模式,重新啟動後您的可攜式資料設定檔將被刪除,漫遊資料設定檔會保留 + Flow Launcher 需要重新啟動以完成啟用可攜式模式,重新啟動後您的漫遊資料設定檔將被刪除,而可攜式資料設定檔則會保留 + Flow Launcher 偵測到您已啟用可攜式模式,是否要將它移到其他位置? + Flow Launcher 偵測到您已停用可攜模式,相關的捷徑與解除安裝程式項目已建立 + Flow Launcher 偵測到您的使用者資料同時存在於 {0} 與 {1}。{2}{2}請刪除 {1} 以繼續。尚未發生任何變更。 - The following plugin has errored and cannot be loaded: - The following plugins have errored and cannot be loaded: - Please refer to the logs for more information + 下列外掛發生錯誤,無法載入: + 下列外掛發生錯誤,無法載入: + 請參閱日誌以獲得更多資訊 - Please try again - Unable to parse Http Proxy + 請再試一次 + 無法解析 Http 代理 - Failed to install TypeScript environment. Please try again later - Failed to install Python environment. Please try again later. + 無法安裝 TypeScript 環境。請稍後再試一次 + 安裝 Python 環境失敗。請稍後再試。 - Failed to register hotkey "{0}". The hotkey may be in use by another program. Change to a different hotkey, or exit another program. - Failed to unregister hotkey "{0}". Please try again or see log for details + 註冊熱鍵「{0}」失敗。此熱鍵可能已被其他程式使用。請更改為不同的熱鍵,或關閉其他程式。 + 無法取消註冊熱鍵「{0}」。請再試一次或查看日誌以取得詳細資訊 Flow Launcher 啟動命令 {0} 失敗 無效的 Flow Launcher 外掛格式 @@ -54,7 +54,7 @@ 複製 剪下 貼上 - Undo + 還原 全選 檔案 資料夾 @@ -62,12 +62,12 @@ 遊戲模式 暫停使用快捷鍵。 重設位置 - Reset search window position - Type here to search - {0}: This plugin is still initializing... - Select this result to requery - {0}: Failed to respond! - Select this result for more info + 重設搜尋視窗位置 + 在此輸入以搜尋 + 「{0}:此外掛程式仍在初始化……」…… + 選擇此結果以重新查詢 + 「{0}:未能回應!」! + 選取此結果以取得更多資訊 設定 @@ -75,22 +75,24 @@ 便攜模式 將所有設定和使用者資料存儲在一個資料夾中(當與可移動磁碟或雲服務一起使用時很有用)。 開機時啟動 - Use logon task instead of startup entry for faster startup experience - After uninstallation, you need to manually remove this task (Flow.Launcher Startup) via Task Scheduler - Error setting launch on startup + 使用登入工作取代啟動項目,以加快啟動體驗 + 解除安裝後,您需要透過工作排程程式手動移除此工作(Flow.Launcher Startup) + 設定開機啟動時發生錯誤 失去焦點時自動隱藏 Flow Launcher + 當 Flow Launcher 開啟時顯示工作列 + 當開啟 Flow Launcher 時暫時顯示工作列,對自動隱藏的工作列很有用。 不顯示新版本提示 - Search Window Location + 搜尋視窗位置 記住最後位置 - Monitor with Mouse Cursor - Monitor with Focused Window - Primary Monitor - Custom Monitor + 帶有滑鼠游標的顯示器 + 以焦點視窗進行監視 + 主顯示器 + 自訂搜尋視窗位置 搜尋視窗在螢幕上的位置 - Center - Center Top - Left Top - Right Top + 置中 + 頂部置中 + 左側置中 + 右側置中 自訂搜尋視窗位置 語言 最後查詢樣式 @@ -98,10 +100,10 @@ 保留上一個查詢 選擇上一個查詢 清空上次搜尋關鍵字 - Preserve Last Action Keyword - Select Last Action Keyword + 保留上次操作的關鍵字 + 選擇最後操作的關鍵字 最大結果顯示個數 - You can also quickly adjust this by using CTRL+Plus and CTRL+Minus. + 您也可以使用「CTRL+加號」和「CTRL+減號」快速調整此設定。 全螢幕模式下忽略快捷鍵 全螢幕模式下停用快捷鍵(推薦用於遊戲時)。 預設檔案管理器 @@ -109,28 +111,28 @@ 預設瀏覽器 設定新增分頁、視窗和無痕模式。 Python 位置 - Node.js Path - Please select the Node.js executable + Node.js的路徑 + 請選擇Node.js的可執行檔 請選擇 pythonw.exe 一律以英文模式開始輸入 啟動 Flow 時暫時將輸入法切換為英文模式。 自動更新 - Automatically check and update the app when available + 在可用時自動檢查並更新應用程式 選擇 啟動時不顯示主視窗 - Flow Launcher search window is hidden in the tray after starting up. + 啟動後,Flow Launcher搜尋視窗會隱藏在系統匣中。 隱藏任務欄圖示 當圖示從系統列隱藏時,可以透過在搜尋視窗上按右鍵來開啟設定選單。 查詢搜尋精確度 更改結果所需的最低匹配分數。 - None - Low - Regular + + + 一般 拼音搜尋 - Pinyin is the standard system of romanized spelling for translating Chinese. Please note, enabling this can significantly increase memory usage during search. - Use Double Pinyin - Use Double Pinyin instead of Full Pinyin to search. - Double Pinyin Schema + 拼音是中文翻譯的標準羅馬化拼字系統。請注意,啟用此功能可能會顯著增加搜尋時的記憶體使用量。 + 啟用雙拼模式 + 請使用雙拼以搜尋,而不是全拼搜尋。 + 雙拼結構 Xiao He Zi Ran Ma Wei Ruan @@ -143,12 +145,12 @@ 一律預覽 當 Flow 啟動時,一律開啟預覽面板。按下 {0} 可切換預覽。 - Shadow effect is not allowed while current theme has blur effect enabled - Search Delay - Adds a short delay while typing to reduce UI flicker and result load. Recommended if your typing speed is average. - Enter the wait time (in ms) until input is considered complete. This can only be edited if Search Delay is enabled. - Default Search Delay Time - Wait time before showing results after typing stops. Higher values wait longer. (ms) + 當目前主題啟用模糊效果時,將不允許使用陰影效果 + 延遲搜尋 + 在輸入時增加短暫延遲,以減少介面閃爍和結果載入時間。建議打字速度中等的使用者使用。 + 輸入等待時間(以毫秒為單位),直到認為輸入完成為止。僅當啟用“搜尋延遲”時才能編輯此設定。 + 預設搜尋延遲時間 + 輸入停止後顯示結果前的等待時間。數值越高,等待時間越長。 (以毫秒為單位) Information for Korean IME user The Korean input method used in Windows 11 may cause some issues in Flow Launcher. @@ -164,17 +166,17 @@ - Open Language and Region System Settings - Opens the Korean IME setting location. Go to Korean > Language Options > Keyboard - Microsoft IME > Compatibility + 開啟語言和區域設定 + 開啟韓語輸入法設定。前往“韓語”>“語言選項”>“鍵盤 - Microsoft 輸入法”>“相容性”。 開啟 - Use Previous Korean IME - You can change the Previous Korean IME settings directly from here - Failed to change Korean IME setting - Please check your system registry access or contact support. - Home Page - Show home page results when query text is empty. - Show History Results in Home Page - Maximum History Results Shown in Home Page + 使用舊版韓語輸入法 + 您可以直接從這裡更改先前的韓語輸入法設定 + 更改韓語輸入法設定失敗 + 請檢查您的系統註冊表存取權限或尋求協助。 + 首頁 + 當查詢文字為空時,顯示首頁結果。 + 在首頁顯示歷史記錄 + 首頁顯示最多歷史搜尋結果 History Style Choose the type of history to show in the History and Home Page Query history @@ -209,8 +211,8 @@ Advanced Settings: 已啟用 優先 - Search Delay - Home Page + 延遲搜尋 + 首頁 目前優先 新增優先 優先 @@ -263,21 +265,21 @@ Plugin uninstall {0} by {1} {2}{2}Would you like to uninstall this plugin? Plugin update - {0} by {1} {2}{2}Would you like to update this plugin? + 您想更新{0} (由 {1} {2}{2} 製作)嗎? 正在下載擴充功能 - Automatically restart after installing/uninstalling/updating plugins in plugin store - Zip file does not have a valid plugin.json configuration - Installing from an unknown source - This plugin is from an unknown source and it may contain potential risks!{0}{0}Please ensure you understand where this plugin is from and that it is safe.{0}{0}Would you like to continue still?{0}{0}(You can switch off this warning in general section of setting window) - Zip files - Please select zip file - Install plugin from local path + 在外掛商店安裝/卸載/更新插件後自動重啟 + 壓縮檔中沒有有效的plugin.json配置 + 從未知來源安裝 + 您正在安裝來自未知來源的插件,它可能有潛在風險!{0}{0} 請確保您了解此插件的來源並確認其安全性。{0}{0} 是否繼續? {0}{0}(您可以在設定中關閉此警告) + 壓縮檔 + 請選擇一個壓縮檔 + 從本地路徑安裝外掛 無可用更新 所有插件均為最新版本 - Plugin updates available - Update plugins - Check plugin updates - Plugins are successfully updated. Please restart Flow. + 有外掛更新可用 + 更新外掛程式 + 檢查外掛更新 + 插件已成功更新。請重新啟動Flow。 主題 @@ -288,21 +290,21 @@ 檔案總管 搜尋檔案、資料夾和檔案內容 網路搜尋 - Search the web with different search engine support + 使用不同的搜尋引擎搜尋網絡 程式 以系統管理員或其他使用者啟用應用程式 ProcessKiller - Terminate unwanted processes - Search Bar Height - Item Height + 終止不需要的進程 + 搜尋列高度 + 項目高度 查詢框字體 - Result Title Font - Result Subtitle Font - Reset - Reset to the recommended font and size settings. - Import Theme Size - If a size value intended by the theme designer is available, it will be retrieved and applied. - Customize + 結果標題字體 + 結果副標題字體 + 重設 + 恢復預設字體與文字大小設定。 + 匯入主題大小 + 如果主題設計者預設的尺寸值存在,則會擷取並套用該尺寸值。 + 個人化 視窗模式 透明度 找不到主題 {0} ,將回到預設主題 @@ -315,49 +317,49 @@ 暗色系 音效 搜尋窗口打開時播放音效 - Sound Effect Volume - Adjust the volume of the sound effect - Windows Media Player is unavailable and is required for Flow's volume adjustment. Please check your installation if you need to adjust volume. + 音效音量 + 調整音效音量 + Windows Media Player不可用,而Flow的音量調整功能需要它。如果您需要調整音量,請檢查您的安裝情況。 動畫 使用介面動畫 - Animation Speed - The speed of the UI animation - Slow - Medium - Fast - Custom + 動畫速度 + UI動畫速度 + 慢速 + 中等 + 快速 + 自訂 時鐘 日期 - Backdrop Type - The backdrop effect is not applied in the preview. - Backdrop supported starting from Windows 11 build 22000 and above - None - Acrylic - Mica - Mica Alt - This theme supports two (light/dark) modes. - This theme supports Blur Transparent Background. - Show placeholder - Display placeholder when query is empty - Placeholder text - Change placeholder text. Input empty will use: {0} - Fixed Window Size - The window size is not adjustable by dragging. - Since Always Preview is on, maximum results shown may not take effect because preview panel requires a certain minimum height + 背景類型 + 預覽中未套用背景效果。 + 從 Windows 11 版本 22000 及更高版本開始支援背景功能 + + 壓克力 + 雲母 + 雲母(替代樣式) + 此主題支援兩種(淺色/深色)模式。 + 此主題支援模糊透明背景。 + 顯示佔位符 + 當查詢為空時顯示佔位符 + 佔位符文字 + 更改佔位符文字。輸入為空將使用{0} + 固定視窗大小 + 視窗大小無法透過拖曳進行調整。 + 由於「始終預覽」已啟用,因此可能無法顯示最大效果,因為預覽面板需要一定的最小高度 快捷鍵 快捷鍵 - Open Flow Launcher + 開啟Flow Launcher 執行縮寫以顯示 / 隱藏 Flow Launcher。 - Toggle Preview + 切換預覽 Enter shortcut to show/hide preview in search window. Hotkey Presets List of currently registered hotkeys 開放結果修飾符 - Select a modifier key to open selected result via keyboard. + 選擇一個修飾鍵以透過鍵盤開啟已選擇的結果。 顯示快捷鍵 - Show result selection hotkey with results. + 利用結果來顯示選擇的快捷鍵結果。 Auto Complete Runs autocomplete for the selected items. Select Next Item @@ -541,7 +543,7 @@ Input the search delay time in ms you like to use for the plugin. Input empty if you don't want to specify any, and the plugin will use default search delay time. - Home Page + 首頁 Enable the plugin home page state if you like to show the plugin results when query is empty. @@ -655,12 +657,12 @@ If you add an '@' prefix while inputting a shortcut, it matches any position in 返回 / 快捷選單 - Item Navigation + 物件導覽 打開選單 開啟檔案位置 Run as Admin / Open Folder in Default File Manager 查詢歷史 - Back to Result in Context Menu + 返回右鍵選單中的結果 自動完成 開啟/運行選擇項目 開啟視窗設定 diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index dd47f9d4e..747975b2a 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -526,7 +526,7 @@ + Text="{Binding PreviewDescription}" /> diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index d6e5f7cdb..101a47efc 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -477,7 +477,7 @@ namespace Flow.Launcher && QueryTextBox.CaretIndex == QueryTextBox.Text.Length) { var queryWithoutActionKeyword = - QueryBuilder.Build(QueryTextBox.Text.Trim(), PluginManager.GetNonGlobalPlugins())?.Search; + QueryBuilder.Build(QueryTextBox.Text, QueryTextBox.Text.Trim(), PluginManager.GetNonGlobalPlugins())?.Search; if (FilesFolders.IsLocationPathString(queryWithoutActionKeyword)) { diff --git a/Flow.Launcher/ResultListBox.xaml.cs b/Flow.Launcher/ResultListBox.xaml.cs index ac51b195c..fcc73e9ce 100644 --- a/Flow.Launcher/ResultListBox.xaml.cs +++ b/Flow.Launcher/ResultListBox.xaml.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Threading; using System.Windows; using System.Windows.Controls; using System.Windows.Input; @@ -9,7 +10,7 @@ namespace Flow.Launcher { public partial class ResultListBox { - protected object _lock = new object(); + protected Lock _lock = new(); private Point _lastpos; private ListBoxItem curItem = null; public ResultListBox() @@ -88,12 +89,11 @@ namespace Flow.Launcher } } - - private Point start; - private string path; - private string query; + private Point _start; + private string _path; + private string _trimmedQuery; // this method is called by the UI thread, which is single threaded, so we can be sloppy with locking - private bool isDragging; + private bool _isDragging; private void ResultList_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { @@ -104,53 +104,55 @@ namespace Flow.Launcher Result: { CopyText: { } copyText, - OriginQuery.RawQuery: { } rawQuery + OriginQuery.TrimmedQuery: { } trimmedQuery } } }) return; - path = copyText; - query = rawQuery; - start = e.GetPosition(null); - isDragging = true; + _path = copyText; + _trimmedQuery = trimmedQuery; + _start = e.GetPosition(null); + _isDragging = true; } + private void ResultList_MouseMove(object sender, MouseEventArgs e) { - if (e.LeftButton != MouseButtonState.Pressed || !isDragging) + if (e.LeftButton != MouseButtonState.Pressed || !_isDragging) { - start = default; - path = string.Empty; - query = string.Empty; - isDragging = false; + _start = default; + _path = string.Empty; + _trimmedQuery = string.Empty; + _isDragging = false; return; } - if (!File.Exists(path) && !Directory.Exists(path)) + if (!File.Exists(_path) && !Directory.Exists(_path)) return; Point mousePosition = e.GetPosition(null); - Vector diff = this.start - mousePosition; + Vector diff = _start - mousePosition; if (Math.Abs(diff.X) < SystemParameters.MinimumHorizontalDragDistance || Math.Abs(diff.Y) < SystemParameters.MinimumVerticalDragDistance) return; - isDragging = false; + _isDragging = false; App.API.HideMainWindow(); var data = new DataObject(DataFormats.FileDrop, new[] { - path + _path }); // Reassigning query to a new variable because for some reason // after DragDrop.DoDragDrop call, 'query' loses its content, i.e. becomes empty string - var rawQuery = query; + var trimmedQuery = _trimmedQuery; var effect = DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Move | DragDropEffects.Copy); if (effect == DragDropEffects.Move) - App.API.ChangeQuery(rawQuery, true); + App.API.ChangeQuery(trimmedQuery, true); } + private void ResultListBox_OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) { if (Mouse.DirectlyOver is not FrameworkElement { DataContext: ResultViewModel result }) @@ -158,6 +160,7 @@ namespace Flow.Launcher RightClickResultCommand?.Execute(result.Result); } + private void ResultListBox_OnPreviewMouseUp(object sender, MouseButtonEventArgs e) { if (Mouse.DirectlyOver is not FrameworkElement { DataContext: ResultViewModel result }) diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml index bf284b1a6..513936f14 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml @@ -86,6 +86,16 @@ OnContent="{DynamicResource enable}" /> + + + + + Source="{Binding IcoPathAbsolute, IsAsync=True}" /> +/// A serializable result used to record the last opened history for reopening results. +/// Inherits common result fields from and adds the original query and execution time. +/// +public class LastOpenedHistoryResult : Result +{ + /// + /// The query string from Query.TrimmedQuery property, it is stored as a string instead of the entire Query class . + /// This is used so results can be reopened or re-run using the serialized query string. + /// + public string Query { get; set; } = string.Empty; + + /// + /// The local date and time when this result was executed/opened. + /// + public DateTime ExecutedDateTime { get; set; } + + /// + /// Initializes a new instance of . + /// + public LastOpenedHistoryResult() + { + } + + /// + /// Creates a from an existing . + /// Copies required fields and sets up default reopening actions. + /// + /// The original result to create history from. + public LastOpenedHistoryResult(Result result) + { + Title = result.Title; + SubTitle = result.SubTitle; + PluginID = result.PluginID; + Query = result.OriginQuery.TrimmedQuery; + OriginQuery = result.OriginQuery; + RecordKey = result.RecordKey; + IcoPath = result.IcoPath; + PluginDirectory = result.PluginDirectory; + Glyph = result.Glyph; + ExecutedDateTime = DateTime.Now; + // Used for Query History style reopening + Action = _ => + { + App.API.BackToQueryResults(); + App.API.ChangeQuery(result.OriginQuery.TrimmedQuery); + return false; + }; + // Used for Last Opened History style reopening, currently need to be assigned at MainViewModel.cs + AsyncAction = null; + } + + /// + /// Selectively creates a deep copy of the required properties for + /// based on the style of history- Last Opened or Query. + /// This copy should be independent of original and full isolated. + /// + /// A new containing the same required data. + public LastOpenedHistoryResult DeepCopyForHistoryStyle(bool isHistoryStyleLastOpened) + { + // queryValue and glyphValue are captured to ensure they are correctly referenced in the Action delegate. + var queryValue = Query; + var glyphValue = Glyph; + + var title = string.Empty; + var showBadge = false; + var badgeIcoPath = string.Empty; + var icoPath = string.Empty; + var glyph = null as GlyphInfo; + + if (isHistoryStyleLastOpened) + { + title = Title; + icoPath = IcoPath; + glyph = glyphValue != null + ? new GlyphInfo(glyphValue.FontFamily, glyphValue.Glyph) + : null; + showBadge = true; + badgeIcoPath = Constant.HistoryIcon; + } + else + { + title = Localize.executeQuery(Query); + icoPath = Constant.HistoryIcon; + glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\uE81C"); + showBadge = false; + } + + return new LastOpenedHistoryResult + { + Title = title, + // Subtitle has datetime which can cause duplicates when saving. + SubTitle = Localize.lastExecuteTime(ExecutedDateTime), + // Empty PluginID so the source of last opened history results won't be updated, this copy is meant to be temporary. + PluginID = string.Empty, + Query = Query, + OriginQuery = new Query { TrimmedQuery = Query }, + RecordKey = RecordKey, + IcoPath = icoPath, + ShowBadge = showBadge, + BadgeIcoPath = badgeIcoPath, + PluginDirectory = PluginDirectory, + // Used for Query History style reopening + Action = _ => + { + App.API.BackToQueryResults(); + App.API.ChangeQuery(queryValue); + return false; + }, + // Used for Last Opened History style reopening, currently need to be assigned at MainViewModel.cs + AsyncAction = null, + Glyph = glyph, + ExecutedDateTime = ExecutedDateTime + // Note: Other properties are left as default — copy if needed. + }; + } + + /// + /// Determines whether the specified is equivalent to this history result. + /// Comparison uses when available; otherwise falls back to title/subtitle/plugin id and query. + /// + /// The result to compare to. + /// true if the results are considered equal; otherwise false. + public bool Equals(Result r) + { + if (string.IsNullOrEmpty(RecordKey) || string.IsNullOrEmpty(r.RecordKey)) + { + return Title == r.Title + && SubTitle == r.SubTitle + && PluginID == r.PluginID + && Query == r.OriginQuery.TrimmedQuery; + } + else + { + return RecordKey == r.RecordKey + && PluginID == r.PluginID + && Query == r.OriginQuery.TrimmedQuery; + } + } +} diff --git a/Flow.Launcher/Storage/QueryHistory.cs b/Flow.Launcher/Storage/QueryHistory.cs index 8284f7eea..d9a527f61 100644 --- a/Flow.Launcher/Storage/QueryHistory.cs +++ b/Flow.Launcher/Storage/QueryHistory.cs @@ -1,7 +1,8 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text.Json.Serialization; +using Flow.Launcher.Core.Plugin; using Flow.Launcher.Plugin; namespace Flow.Launcher.Storage @@ -14,28 +15,50 @@ namespace Flow.Launcher.Storage #pragma warning restore CS0618 // Type or member is obsolete [JsonInclude] - public List LastOpenedHistoryItems { get; private set; } = []; + public List LastOpenedHistoryItems { get; private set; } = []; private readonly int _maxHistory = 300; + /// + /// Migrate legacy history data (stored in ) into the new + /// format and append them to + /// . + /// + [Obsolete("For backwards compatibility. Remove after release v2.3.0")] public void PopulateHistoryFromLegacyHistory() { if (Items.Count == 0) return; // Migrate old history items to new LastOpenedHistoryItems foreach (var item in Items) { - LastOpenedHistoryItems.Add(new LastOpenedHistoryItem + LastOpenedHistoryItems.Add(new LastOpenedHistoryResult { + Title = Localize.executeQuery(item.Query), + OriginQuery = new Query { TrimmedQuery = item.Query }, Query = item.Query, + Action = _ => + { + App.API.BackToQueryResults(); + App.API.ChangeQuery(item.Query); + return false; + }, ExecutedDateTime = item.ExecutedDateTime }); } Items.Clear(); } + /// + /// Records a result into the last-opened history list (). + /// This will also update the IcoPath if existing history item has one that is different. + /// + /// The result to add to history. Must have a non-empty .. public void Add(Result result) { - if (string.IsNullOrEmpty(result.OriginQuery.RawQuery)) return; + if (string.IsNullOrEmpty(result.OriginQuery.TrimmedQuery)) return; + // History results triggered from homepage do not contain PluginID, + // these are intentionally not saved otherwise cause duplicates due to subtitle + // containing datetime string. if (string.IsNullOrEmpty(result.PluginID)) return; // Maintain the max history limit @@ -44,23 +67,53 @@ namespace Flow.Launcher.Storage LastOpenedHistoryItems.RemoveAt(0); } - // If the last item is the same as the current result, just update the timestamp - if (LastOpenedHistoryItems.Count > 0 && - LastOpenedHistoryItems.Last().Equals(result)) + // If the last item is the same as the current result, just update the timestamp and the icon path + if (LastOpenedHistoryItems.Count > 0 && + TryGetLastOpenedHistoryResult(result, out var existingHistoryItem)) { - LastOpenedHistoryItems.Last().ExecutedDateTime = DateTime.Now; + existingHistoryItem.ExecutedDateTime = DateTime.Now; + + if (existingHistoryItem.IcoPath != result.IcoPath) + existingHistoryItem.IcoPath = result.IcoPath; + + if (existingHistoryItem.Glyph?.Glyph != result.Glyph?.Glyph + || existingHistoryItem.Glyph?.FontFamily != result.Glyph?.FontFamily) + existingHistoryItem.SetGlyph(result.Glyph); } else { - LastOpenedHistoryItems.Add(new LastOpenedHistoryItem - { - Title = result.Title, - SubTitle = result.SubTitle, - PluginID = result.PluginID, - Query = result.OriginQuery.RawQuery, - RecordKey = result.RecordKey, - ExecutedDateTime = DateTime.Now - }); + LastOpenedHistoryItems.Add(new LastOpenedHistoryResult(result)); + } + } + + /// + /// Attempts to find an existing in + /// that is considered equal to the supplied . + /// + private bool TryGetLastOpenedHistoryResult(Result result, out LastOpenedHistoryResult historyItem) + { + historyItem = LastOpenedHistoryItems.FirstOrDefault(x => x.Equals(result)); + return historyItem is not null; + } + + /// + /// Flow uses IcoPathAbsolute property to display result the icons. This refreshes the IcoPathAbsolute + /// property using current plugin metadata by updating the PluginDirectory property, which in turn also + /// updates IcoPath. This keeps the saved icon paths of results updated correctly if flow is moved around. + /// + /// Call this after plugins are loaded/initialized. + public void UpdateIcoPathAbsolute() + { + if (LastOpenedHistoryItems.Count == 0) return; + + foreach (var item in LastOpenedHistoryItems) + { + if (string.IsNullOrEmpty(item.PluginID)) continue; + + var pluginPair = PluginManager.GetPluginForId(item.PluginID); + if (pluginPair == null) continue; + + item.PluginDirectory = pluginPair.Metadata.PluginDirectory; } } } diff --git a/Flow.Launcher/Storage/TopMostRecord.cs b/Flow.Launcher/Storage/TopMostRecord.cs index 7fc9dcaaa..9708eab97 100644 --- a/Flow.Launcher/Storage/TopMostRecord.cs +++ b/Flow.Launcher/Storage/TopMostRecord.cs @@ -113,7 +113,7 @@ namespace Flow.Launcher.Storage internal bool IsTopMost(Result result) { - if (records.IsEmpty || !records.TryGetValue(result.OriginQuery.RawQuery, out var value)) + if (records.IsEmpty || !records.TryGetValue(result.OriginQuery.TrimmedQuery, out var value)) { return false; } @@ -124,7 +124,7 @@ namespace Flow.Launcher.Storage internal void Remove(Result result) { - records.Remove(result.OriginQuery.RawQuery, out _); + records.Remove(result.OriginQuery.TrimmedQuery, out _); } internal void AddOrUpdate(Result result) @@ -136,7 +136,7 @@ namespace Flow.Launcher.Storage SubTitle = result.SubTitle, RecordKey = result.RecordKey }; - records.AddOrUpdate(result.OriginQuery.RawQuery, record, (key, oldValue) => record); + records.AddOrUpdate(result.OriginQuery.TrimmedQuery, record, (key, oldValue) => record); } } @@ -154,7 +154,7 @@ namespace Flow.Launcher.Storage // origin query is null when user select the context menu item directly of one item from query list // in this case, we do not need to check if the result is top most if (records.IsEmpty || result.OriginQuery == null || - !records.TryGetValue(result.OriginQuery.RawQuery, out var value)) + !records.TryGetValue(result.OriginQuery.TrimmedQuery, out var value)) { return false; } @@ -168,7 +168,7 @@ namespace Flow.Launcher.Storage // origin query is null when user select the context menu item directly of one item from query list // in this case, we do not need to check if the result is top most if (records.IsEmpty || result.OriginQuery == null || - !records.TryGetValue(result.OriginQuery.RawQuery, out var value)) + !records.TryGetValue(result.OriginQuery.TrimmedQuery, out var value)) { return -1; } @@ -194,7 +194,7 @@ namespace Flow.Launcher.Storage // origin query is null when user select the context menu item directly of one item from query list // in this case, we do not need to remove the record if (result.OriginQuery == null || - !records.TryGetValue(result.OriginQuery.RawQuery, out var value)) + !records.TryGetValue(result.OriginQuery.TrimmedQuery, out var value)) { return; } @@ -204,12 +204,12 @@ namespace Flow.Launcher.Storage if (queue.IsEmpty) { // if the queue is empty, remove the queue from the dictionary - records.TryRemove(result.OriginQuery.RawQuery, out _); + records.TryRemove(result.OriginQuery.TrimmedQuery, out _); } else { // change the queue in the dictionary - records[result.OriginQuery.RawQuery] = queue; + records[result.OriginQuery.TrimmedQuery] = queue; } } @@ -229,19 +229,19 @@ namespace Flow.Launcher.Storage SubTitle = result.SubTitle, RecordKey = result.RecordKey }; - if (!records.TryGetValue(result.OriginQuery.RawQuery, out var value)) + if (!records.TryGetValue(result.OriginQuery.TrimmedQuery, out var value)) { // create a new queue if it does not exist value = new ConcurrentQueue(); value.Enqueue(record); - records.TryAdd(result.OriginQuery.RawQuery, value); + records.TryAdd(result.OriginQuery.TrimmedQuery, value); } else { // add or update the record in the queue var queue = new ConcurrentQueue(value.Where(r => !r.Equals(result))); // make sure we don't have duplicates queue.Enqueue(record); - records[result.OriginQuery.RawQuery] = queue; + records[result.OriginQuery.TrimmedQuery] = queue; } } } diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index f0f4b257a..333ac3652 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; @@ -35,17 +36,18 @@ namespace Flow.Launcher.ViewModel private static readonly string ClassName = nameof(MainViewModel); - private bool _isQueryRunning; private Query _lastQuery; private bool _previousIsHomeQuery; + private readonly ConcurrentDictionary _progressQueryDict = new(); // Used for QueryResultAsync + private Query _updateQuery; // Used for ResultsUpdated private string _queryTextBeforeLeaveResults; private string _ignoredQueryText; // Used to ignore query text change when switching between context menu and query results private readonly FlowLauncherJsonStorage _historyItemsStorage; - private readonly FlowLauncherJsonStorage _userSelectedRecordStorage; - private readonly FlowLauncherJsonStorageTopMostRecord _topMostRecord; private readonly History _history; private int lastHistoryIndex = 1; + private readonly FlowLauncherJsonStorage _userSelectedRecordStorage; + private readonly FlowLauncherJsonStorageTopMostRecord _topMostRecord; private readonly UserSelectedRecord _userSelectedRecord; private CancellationTokenSource _updateSource; // Used to cancel old query flows @@ -63,6 +65,8 @@ namespace Flow.Launcher.ViewModel Priority = 0 // Priority is for calculating scores in UpdateResultView }; + private bool _taskbarShownByFlow = false; + #endregion #region Constructor @@ -149,11 +153,10 @@ namespace Flow.Launcher.ViewModel }; _historyItemsStorage = new FlowLauncherJsonStorage(); - _userSelectedRecordStorage = new FlowLauncherJsonStorage(); - _topMostRecord = new FlowLauncherJsonStorageTopMostRecord(); _history = _historyItemsStorage.Load(); - _history.PopulateHistoryFromLegacyHistory(); + _userSelectedRecordStorage = new FlowLauncherJsonStorage(); _userSelectedRecord = _userSelectedRecordStorage.Load(); + _topMostRecord = new FlowLauncherJsonStorageTopMostRecord(); ContextMenu = new ResultsViewModel(Settings, this) { @@ -283,7 +286,7 @@ namespace Flow.Launcher.ViewModel plugin.ResultsUpdated += (s, e) => { - if (e.Query.RawQuery != QueryText || e.Token.IsCancellationRequested) + if (_updateQuery == null || e.Query.OriginalQuery != _updateQuery.OriginalQuery || e.Token.IsCancellationRequested) { return; } @@ -352,11 +355,17 @@ namespace Flow.Launcher.ViewModel if (QueryResultsSelected()) { SelectedResults = History; - History.SelectedIndex = _history.LastOpenedHistoryItems.Count - 1; + if (History.Results.Count > 0) + { + SelectedResults.SelectedIndex = 0; + SelectedResults.SelectedItem = History.Results[0]; + } } else { SelectedResults = Results; + PreviewSelectedItem = Results.SelectedItem; + _ = UpdatePreviewAsync(); } } @@ -428,7 +437,8 @@ namespace Flow.Launcher.ViewModel { // When switch to ContextMenu from QueryResults, but no item being chosen, should do nothing // i.e. Shift+Enter/Ctrl+O right after Alt + Space should do nothing - if (SelectedResults.SelectedItem != null) + if (SelectedResults.SelectedItem?.Result != null && + !string.IsNullOrEmpty(SelectedResults.SelectedItem.Result.PluginID)) // Do not show context menu for history results { SelectedResults = ContextMenu; } @@ -436,13 +446,15 @@ namespace Flow.Launcher.ViewModel else { SelectedResults = Results; + PreviewSelectedItem = Results.SelectedItem; + _ = UpdatePreviewAsync(); } } [RelayCommand] private void Backspace(object index) { - var query = QueryBuilder.Build(QueryText.Trim(), PluginManager.GetNonGlobalPlugins()); + var query = QueryBuilder.Build(QueryText, QueryText.Trim(), PluginManager.GetNonGlobalPlugins()); // GetPreviousExistingDirectory does not require trailing '\', otherwise will return empty string var path = FilesFolders.GetPreviousExistingDirectory((_) => true, query.Search.TrimEnd('\\')); @@ -639,6 +651,8 @@ namespace Flow.Launcher.ViewModel if (!QueryResultsSelected()) { SelectedResults = Results; + PreviewSelectedItem = Results.SelectedItem; + _ = UpdatePreviewAsync(); } else { @@ -1249,22 +1263,12 @@ namespace Flow.Launcher.ViewModel var selected = Results.SelectedItem?.Result; - if (selected != null) // SelectedItem returns null if selection is empty. + if (selected != null && // SelectedItem returns null if selection is empty. + !string.IsNullOrEmpty(selected.PluginID)) // SelectedItem must have a valid PluginID, history results do not. { - List results; - if (selected.PluginID == null) // SelectedItem from history in home page. - { - results = new() - { - ContextMenuTopMost(selected) - }; - } - else - { - results = PluginManager.GetContextMenusForPlugin(selected); - results.Add(ContextMenuTopMost(selected)); - results.Add(ContextMenuPluginInfo(selected)); - } + List results = PluginManager.GetContextMenusForPlugin(selected); + results.Add(ContextMenuTopMost(selected)); + results.Add(ContextMenuPluginInfo(selected)); if (!string.IsNullOrEmpty(query)) { @@ -1315,68 +1319,77 @@ namespace Flow.Launcher.ViewModel } } - private List GetHistoryItems(IEnumerable historyItems) + private List GetHistoryItems(IEnumerable historyItems, int? maxResult = null) { var results = new List(); - if (Settings.HistoryStyle == HistoryStyle.Query) - { - foreach (var h in historyItems) - { - var result = new Result - { - Title = Localize.executeQuery(h.Query), - SubTitle = Localize.lastExecuteTime(h.ExecutedDateTime), - IcoPath = Constant.HistoryIcon, - OriginQuery = new Query { RawQuery = h.Query }, - Action = _ => - { - App.API.BackToQueryResults(); - App.API.ChangeQuery(h.Query); - return false; - }, - Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\uE81C") - }; - results.Add(result); - } - } - else - { - foreach (var h in historyItems) - { - var result = new Result - { - Title = string.IsNullOrEmpty(h.Title) ? // Old migrated history items have no title - Localize.executeQuery(h.Query) : - h.Title, - SubTitle = Localize.lastExecuteTime(h.ExecutedDateTime), - IcoPath = Constant.HistoryIcon, - OriginQuery = new Query { RawQuery = h.Query }, - AsyncAction = async c => - { - var reflectResult = await ResultHelper.PopulateResultsAsync(h); - if (reflectResult != null) - { - // Record the user selected record for result ranking - _userSelectedRecord.Add(reflectResult); - // Since some actions may need to hide the Flow window to execute - // So let us populate the results of them - return await reflectResult.ExecuteAsync(c); - } + // Order by executed time descending: Latest -> Oldest + historyItems = historyItems.OrderByDescending(x => x.ExecutedDateTime); - // If we cannot get the result, fallback to re-query - App.API.BackToQueryResults(); - App.API.ChangeQuery(h.Query); - return false; - }, - Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\uE81C") - }; - results.Add(result); - } + if (Settings.HistoryStyle == HistoryStyle.LastOpened) + { + // Items saved to disk are differentiated by Query also, but LastOpened style only cares about unique results + historyItems = historyItems + .GroupBy(r => new { r.Title, r.SubTitle, r.PluginID, r.RecordKey }) + .Select(g => g.First()); } + + // Max history results to return for display + if (maxResult.HasValue) + { + historyItems = historyItems.Take(maxResult.Value); + } + + foreach (var item in historyItems) + { + var copiedItem = item.DeepCopyForHistoryStyle(Settings.HistoryStyle == HistoryStyle.LastOpened); + + if (Settings.HistoryStyle == HistoryStyle.LastOpened) + { + copiedItem.AsyncAction = async c => + { + // Use original history item to reflect correct result because properties like subtitle have been modified in copiedItem + var reflectResult = await ResultHelper.PopulateResultsAsync(item); + if (reflectResult != null) + { + // Since some actions may need to hide the Flow window to execute + // So let us populate the results of them + return await reflectResult.ExecuteAsync(c); + } + + // If we cannot get the result, fallback to re-query + App.API.BackToQueryResults(); + App.API.ChangeQuery(copiedItem.Query); + return false; + }; + } + + results.Add(copiedItem); + } + return results; } + /// + /// Refreshes the last-opened history storage by migrating legacy entries and + /// updating stored icon paths to their resolved (absolute) locations. + /// + /// + /// Calls to refresh absolute icon + /// paths on the migrated/saved history entries by updating each item's + /// PluginDirectory (which in turn resolves IcoPathAbsolute). + /// + /// Important: + /// - Plugins must be initialized (their metadata and PluginDirectory set) + /// before calling this method; otherwise icon resolution cannot be performed. + /// + internal void RefreshLastOpenedHistoryResults() + { + _history.PopulateHistoryFromLegacyHistory(); + + _history.UpdateIcoPathAbsolute(); + } + private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, bool reSelect = true) { _updateSource?.Cancel(); @@ -1391,7 +1404,7 @@ namespace Flow.Launcher.ViewModel return; } - App.API.LogDebug(ClassName, $"Start query with ActionKeyword <{query.ActionKeyword}> and RawQuery <{query.RawQuery}>"); + App.API.LogDebug(ClassName, $"Start query with ActionKeyword <{query.ActionKeyword}> and TrimmedQuery <{query.TrimmedQuery}>"); var currentIsHomeQuery = query.IsHomeQuery; var currentIsDialogJump = _isDialogJump; @@ -1403,69 +1416,77 @@ namespace Flow.Launcher.ViewModel return; } - _updateSource?.Dispose(); + // Create a Guid for this update session so that we can filter out in progress checking + var updateGuid = Guid.NewGuid(); - var currentUpdateSource = new CancellationTokenSource(); - _updateSource = currentUpdateSource; - var currentCancellationToken = _updateSource.Token; - _updateToken = currentCancellationToken; - - ProgressBarVisibility = Visibility.Hidden; - _isQueryRunning = true; - - // Switch to ThreadPool thread - await TaskScheduler.Default; - - if (currentCancellationToken.IsCancellationRequested) return; - - // Update the query's IsReQuery property to true if this is a re-query - query.IsReQuery = isReQuery; - - ICollection plugins = Array.Empty(); - if (currentIsHomeQuery) + try { - if (Settings.ShowHomePage) - { - plugins = PluginManager.ValidPluginsForHomeQuery(); - } + _updateSource?.Dispose(); - PluginIconPath = null; - PluginIconSource = null; - SearchIconVisibility = Visibility.Visible; - } - else - { - plugins = PluginManager.ValidPluginsForQuery(query, currentIsDialogJump); + var currentUpdateSource = new CancellationTokenSource(); + _updateSource = currentUpdateSource; + var currentCancellationToken = _updateSource.Token; + _updateToken = currentCancellationToken; - if (plugins.Count == 1) - { - PluginIconPath = plugins.Single().Metadata.IcoPath; - PluginIconSource = await App.API.LoadImageAsync(PluginIconPath); - SearchIconVisibility = Visibility.Hidden; - } - else + ProgressBarVisibility = Visibility.Hidden; + + _progressQueryDict.TryAdd(updateGuid, query); + _updateQuery = query; + + // Switch to ThreadPool thread + await TaskScheduler.Default; + + if (currentCancellationToken.IsCancellationRequested) return; + + // Update the query's IsReQuery property to true if this is a re-query + query.IsReQuery = isReQuery; + + ICollection plugins = Array.Empty(); + if (currentIsHomeQuery) { + if (Settings.ShowHomePage) + { + plugins = PluginManager.ValidPluginsForHomeQuery(); + } + PluginIconPath = null; PluginIconSource = null; SearchIconVisibility = Visibility.Visible; } - } + else + { + plugins = PluginManager.ValidPluginsForQuery(query, currentIsDialogJump); - App.API.LogDebug(ClassName, $"Valid <{plugins.Count}> plugins: {string.Join(" ", plugins.Select(x => $"<{x.Metadata.Name}>"))}"); + if (plugins.Count == 1) + { + PluginIconPath = plugins.Single().Metadata.IcoPath; + PluginIconSource = await App.API.LoadImageAsync(PluginIconPath); + SearchIconVisibility = Visibility.Hidden; + } + else + { + PluginIconPath = null; + PluginIconSource = null; + SearchIconVisibility = Visibility.Visible; + } + } - // Do not wait for performance improvement - /*if (string.IsNullOrEmpty(query.ActionKeyword)) - { - // Wait 15 millisecond for query change in global query - // if query changes, return so that it won't be calculated - await Task.Delay(15, currentCancellationToken); - if (currentCancellationToken.IsCancellationRequested) return; - }*/ + App.API.LogDebug(ClassName, $"Valid <{plugins.Count}> plugins: {string.Join(" ", plugins.Select(x => $"<{x.Metadata.Name}>"))}"); - _ = Task.Delay(200, currentCancellationToken).ContinueWith(_ => + // Do not wait for performance improvement + /*if (string.IsNullOrEmpty(query.ActionKeyword)) + { + // Wait 15 millisecond for query change in global query + // if query changes, return so that it won't be calculated + await Task.Delay(15, currentCancellationToken); + if (currentCancellationToken.IsCancellationRequested) return; + }*/ + + _ = Task.Delay(200, currentCancellationToken).ContinueWith(_ => { // start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet - if (_isQueryRunning) + if (_progressQueryDict.TryGetValue(updateGuid, out var progressQuery) && + progressQuery.OriginalQuery == query.OriginalQuery) { ProgressBarVisibility = Visibility.Visible; } @@ -1474,58 +1495,65 @@ namespace Flow.Launcher.ViewModel TaskContinuationOptions.NotOnCanceled, TaskScheduler.Default); - // plugins are ICollection, meaning LINQ will get the Count and preallocate Array + // plugins are ICollection, meaning LINQ will get the Count and preallocate Array - Task[] tasks; - if (currentIsHomeQuery) - { - if (ShouldClearExistingResultsForNonQuery(plugins)) + Task[] tasks; + if (currentIsHomeQuery) { - Results.Clear(); - App.API.LogDebug(ClassName, $"Existing results are cleared for non-query"); + if (ShouldClearExistingResultsForNonQuery(plugins)) + { + // there are no update tasks and so we can directly return + ClearResults(); + return; + } + + tasks = plugins.Select(plugin => plugin.Metadata.HomeDisabled switch + { + false => QueryTaskAsync(plugin, currentCancellationToken), + true => Task.CompletedTask + }).ToArray(); + + // Query history results for home page firstly so it will be put on top of the results + if (Settings.ShowHistoryResultsForHomePage) + { + QueryHistoryTask(currentCancellationToken); + } + } + else + { + tasks = plugins.Select(plugin => plugin.Metadata.Disabled switch + { + false => QueryTaskAsync(plugin, currentCancellationToken), + true => Task.CompletedTask + }).ToArray(); } - tasks = plugins.Select(plugin => plugin.Metadata.HomeDisabled switch + try { - false => QueryTaskAsync(plugin, currentCancellationToken), - true => Task.CompletedTask - }).ToArray(); + // Check the code, WhenAll will translate all type of IEnumerable or Collection to Array, so make an array at first + await Task.WhenAll(tasks); + } + catch (OperationCanceledException) + { + // nothing to do here + } - // Query history results for home page firstly so it will be put on top of the results - if (Settings.ShowHistoryResultsForHomePage) + if (currentCancellationToken.IsCancellationRequested) return; + + // this should happen once after all queries are done so progress bar should continue + // until the end of all querying + _progressQueryDict.Remove(updateGuid, out _); + + if (!currentCancellationToken.IsCancellationRequested) { - QueryHistoryTask(currentCancellationToken); + // update to hidden if this is still the current query + ProgressBarVisibility = Visibility.Hidden; } } - else + finally { - tasks = plugins.Select(plugin => plugin.Metadata.Disabled switch - { - false => QueryTaskAsync(plugin, currentCancellationToken), - true => Task.CompletedTask - }).ToArray(); - } - - try - { - // Check the code, WhenAll will translate all type of IEnumerable or Collection to Array, so make an array at first - await Task.WhenAll(tasks); - } - catch (OperationCanceledException) - { - // nothing to do here - } - - if (currentCancellationToken.IsCancellationRequested) return; - - // this should happen once after all queries are done so progress bar should continue - // until the end of all querying - _isQueryRunning = false; - - if (!currentCancellationToken.IsCancellationRequested) - { - // update to hidden if this is still the current query - ProgressBarVisibility = Visibility.Hidden; + // this ensures the query is removed from the progress tracking dictionary when this query is canceled or completes + _progressQueryDict.Remove(updateGuid, out _); } // Local function @@ -1603,10 +1631,8 @@ namespace Flow.Launcher.ViewModel void QueryHistoryTask(CancellationToken token) { - // Select last history results and revert its order to make sure last history results are on top - var historyItems = _history.LastOpenedHistoryItems.TakeLast(Settings.MaxHistoryResultsToShowForHomePage).Reverse(); - - var results = GetHistoryItems(historyItems); + // Select last history results + var results = GetHistoryItems(_history.LastOpenedHistoryItems, Settings.MaxHistoryResultsToShowForHomePage); if (token.IsCancellationRequested) return; @@ -1625,7 +1651,7 @@ namespace Flow.Launcher.ViewModel { if (string.IsNullOrWhiteSpace(queryText)) { - return QueryBuilder.Build(string.Empty, PluginManager.GetNonGlobalPlugins()); + return QueryBuilder.Build(string.Empty, string.Empty, PluginManager.GetNonGlobalPlugins()); } var queryBuilder = new StringBuilder(queryText); @@ -1645,7 +1671,7 @@ namespace Flow.Launcher.ViewModel // Applying builtin shortcuts await BuildQueryAsync(builtInShortcuts, queryBuilder, queryBuilderTmp); - return QueryBuilder.Build(queryBuilder.ToString().Trim(), PluginManager.GetNonGlobalPlugins()); + return QueryBuilder.Build(queryText, queryBuilder.ToString().Trim(), PluginManager.GetNonGlobalPlugins()); } private async Task BuildQueryAsync(IEnumerable builtInShortcuts, @@ -2122,6 +2148,13 @@ namespace Flow.Launcher.ViewModel { Win32Helper.SwitchToEnglishKeyboardLayout(true); } + + // Show the taskbar if the setting is enabled + if (Settings.ShowTaskbarWhenInvoked && !_taskbarShownByFlow) + { + Win32Helper.ShowTaskbar(); + _taskbarShownByFlow = true; + } } public async void Hide(bool reset = true) @@ -2190,6 +2223,13 @@ namespace Flow.Launcher.ViewModel Win32Helper.RestorePreviousKeyboardLayout(); } + // Hide the taskbar if the setting is enabled + if (_taskbarShownByFlow) + { + Win32Helper.HideTaskbar(); + _taskbarShownByFlow = false; + } + // Delay for a while to make sure clock will not flicker await Task.Delay(50); diff --git a/Flow.Launcher/ViewModel/ResultViewModel.cs b/Flow.Launcher/ViewModel/ResultViewModel.cs index d4382fb7f..077acfce7 100644 --- a/Flow.Launcher/ViewModel/ResultViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultViewModel.cs @@ -141,7 +141,7 @@ namespace Flow.Launcher.ViewModel private bool GlyphAvailable => Glyph is not null; - private bool ImgIconAvailable => !string.IsNullOrEmpty(Result.IcoPath) || Result.Icon is not null; + private bool ImgIconAvailable => !string.IsNullOrEmpty(Result.IcoPathAbsolute) || Result.Icon is not null; private bool BadgeIconAvailable => !string.IsNullOrEmpty(Result.BadgeIcoPath) || Result.BadgeIcon is not null; @@ -236,7 +236,7 @@ namespace Flow.Launcher.ViewModel private async Task LoadImageAsync() { - var imagePath = Result.IcoPath; + var imagePath = Result.IcoPathAbsolute; var iconDelegate = Result.Icon; if (ImageLoader.TryGetValue(imagePath, false, out var img)) { @@ -266,7 +266,7 @@ namespace Flow.Launcher.ViewModel private async Task LoadPreviewImageAsync() { - var imagePath = Result.Preview.PreviewImagePath ?? Result.IcoPath; + var imagePath = Result.Preview.PreviewImagePath ?? Result.IcoPathAbsolute; var iconDelegate = Result.Preview.PreviewDelegate ?? Result.Icon; if (ImageLoader.TryGetValue(imagePath, true, out var img)) { @@ -288,6 +288,8 @@ namespace Flow.Launcher.ViewModel } } + public string PreviewDescription => Result.Preview?.Description ?? Result.SubTitle; + public Result Result { get; } public int ResultProgress { 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 ba547c86a..aff73ea77 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj @@ -51,6 +51,8 @@ $(OutputPath)runtimes\linux-s390x; $(OutputPath)runtimes\linux-x64; $(OutputPath)runtimes\linux-x86; + $(OutputPath)runtimes\linux-musl-riscv64; + $(OutputPath)runtimes\linux-riscv64; $(OutputPath)runtimes\maccatalyst-arm64; $(OutputPath)runtimes\maccatalyst-x64; $(OutputPath)runtimes\osx; @@ -74,6 +76,8 @@ $(PublishDir)runtimes\linux-s390x; $(PublishDir)runtimes\linux-x64; $(PublishDir)runtimes\linux-x86; + $(PublishDir)runtimes\linux-musl-riscv64; + $(PublishDir)runtimes\linux-riscv64; $(PublishDir)runtimes\maccatalyst-arm64; $(PublishDir)runtimes\maccatalyst-x64; $(PublishDir)runtimes\osx; @@ -105,9 +109,9 @@ - - + + - + \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/zh-tw.xaml index e8bc6bcb6..5d2dd561a 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/zh-tw.xaml @@ -6,7 +6,7 @@ 搜尋你的瀏覽器書籤 - Failed to set url in clipboard + 設定剪貼簿網址失敗 書籤資料 @@ -28,6 +28,6 @@ 瀏覽器引擎 如果你沒有使用 Chrome、Firefox 或 Edge,或者使用它們的便攜版,你需要新增書籤資料位置並選擇正確的瀏覽器引擎,才能讓這個擴充功能正常運作。 例如:Brave 瀏覽器的引擎是 Chromium;而它的預設書籤資料位置是「%LOCALAPPDATA%\BraveSoftware\Brave-Browser\UserData」。對於 Firefox 瀏覽器引擎,書籤資料位置是包含 places.sqlite 檔案的 userdata 資料夾。 - Load favicons (can be time consuming during startup) + 載入網站圖示(啟動時可能比較耗時) diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj b/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj index d3ce290ab..f30b24e4f 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj @@ -64,7 +64,7 @@ - + \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ar.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ar.xaml index 324c91972..8387d0ed4 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ar.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ar.xaml @@ -12,6 +12,7 @@ فاصلة (,) نقطة (.) أقصى عدد من المنازل العشرية + Show thousands separator in results Copy failed, please try later Show error message when calculation fails diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/cs.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/cs.xaml index 844c2dc30..aa60e9331 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/cs.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/cs.xaml @@ -12,6 +12,7 @@ Čárka (,) Tečka (.) Desetinná místa + Show thousands separator in results Copy failed, please try later Show error message when calculation fails diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/da.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/da.xaml index 405a39e92..8c125e3f0 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/da.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/da.xaml @@ -12,6 +12,7 @@ Comma (,) Dot (.) Max. decimal places + Show thousands separator in results Copy failed, please try later Show error message when calculation fails diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/de.xaml index 4dc634db2..749e1a325 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/de.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/de.xaml @@ -12,6 +12,7 @@ Komma (,) Punkt (.) Max. Dezimalstellen + Show thousands separator in results Copy failed, please try later Show error message when calculation fails diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml index b12972b1b..5f9a3ca5a 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml @@ -14,6 +14,7 @@ Comma (,) Dot (.) Max. decimal places + Show thousands separator in results Copy failed, please try later Show error message when calculation fails \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es-419.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es-419.xaml index 12b4fdb0a..b59072b5f 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es-419.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es-419.xaml @@ -12,6 +12,7 @@ Coma (,) Punto (.) Número máximo de decimales + Show thousands separator in results Copy failed, please try later Show error message when calculation fails diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es.xaml index eeb8923ba..2fc2732e4 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es.xaml @@ -12,6 +12,7 @@ Coma (,) Punto (.) Número máximo de decimales + Show thousands separator in results Ha fallado la copia, inténtelo más tarde Mostrar mensaje de error cuando falle el cálculo diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/fr.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/fr.xaml index 3219e517a..fb952429c 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/fr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/fr.xaml @@ -12,6 +12,7 @@ Virgule (,) Point (.) Décimales max. + Afficher le séparateur de milliers dans les résultats Échec de la copie, réessayer plus tard Afficher le message d'erreur lorsque le calcul échoue diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/he.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/he.xaml index 7ee027743..2c30e7c71 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/he.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/he.xaml @@ -12,6 +12,7 @@ פסיק (,) נקודה (.) מספר מקסימלי של מקומות עשרוניים + Show thousands separator in results Copy failed, please try later Show error message when calculation fails diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/it.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/it.xaml index a0e61ff32..a8a0a1e2d 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/it.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/it.xaml @@ -12,6 +12,7 @@ Virgola (,) Punto (.) Max. cifre decimali + Show thousands separator in results Copy failed, please try later Show error message when calculation fails diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ja.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ja.xaml index 5c251d241..d55355139 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ja.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ja.xaml @@ -12,6 +12,7 @@ コンマ(,) ドット (.) 小数点以下の最大桁数 + Show thousands separator in results コピーに失敗しました。後でやり直してください 計算に失敗したときにエラーメッセージを表示 diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ko.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ko.xaml index a595f4839..9f95c0105 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ko.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ko.xaml @@ -12,6 +12,7 @@ 쉼표 (,) 마침표 (.) 최대 소수점 아래 자릿 수 + Show thousands separator in results Copy failed, please try later Show error message when calculation fails diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nb.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nb.xaml index 9b6b1f808..b008b14a6 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nb.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nb.xaml @@ -12,6 +12,7 @@ Komma (,) Prikk (.) Maks. desimaler + Show thousands separator in results Copy failed, please try later Show error message when calculation fails diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nl.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nl.xaml index 405a39e92..8c125e3f0 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nl.xaml @@ -12,6 +12,7 @@ Comma (,) Dot (.) Max. decimal places + Show thousands separator in results Copy failed, please try later Show error message when calculation fails diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pl.xaml index 03f50ca23..131da0eb5 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pl.xaml @@ -12,6 +12,7 @@ Przecinek (,) Kropka (.) Maks. liczba miejsc po przecinku + Show thousands separator in results Copy failed, please try later Show error message when calculation fails diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-br.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-br.xaml index 9afc3b784..6f3ab9540 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-br.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-br.xaml @@ -12,6 +12,7 @@ Vírgula (,) Ponto (.) Max. decimal places + Show thousands separator in results Copy failed, please try later Show error message when calculation fails diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-pt.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-pt.xaml index 1201e2555..a0c1a6fe9 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-pt.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-pt.xaml @@ -12,6 +12,7 @@ Vírgula (,) Ponto (.) Número máximo de casas decimais + Mostrar separador dos milhares no resultado Falha ao copiar. Por favor tente mais tarde. Mostrar mensagem de erro se o cálculo falhar diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ru.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ru.xaml index 7b40770cd..0097d54f0 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ru.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ru.xaml @@ -12,6 +12,7 @@ Запятая (,) Точка (.) Макс. число знаков после запятой + Show thousands separator in results Copy failed, please try later Show error message when calculation fails diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sk.xaml index f398ab3e2..1ba75e4bb 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sk.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sk.xaml @@ -12,6 +12,7 @@ Čiarka (,) Bodka (.) Desatinné miesta + Zobraziť vo výsledkoch oddeľovač tisícov Kopírovanie zlyhalo, skúste to neskôr Zobraziť chybovú správu, keď výpočet zlyhá diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sr-Cyrl-RS.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sr-Cyrl-RS.xaml index 405a39e92..8c125e3f0 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sr-Cyrl-RS.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sr-Cyrl-RS.xaml @@ -12,6 +12,7 @@ Comma (,) Dot (.) Max. decimal places + Show thousands separator in results Copy failed, please try later Show error message when calculation fails diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sr.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sr.xaml index 405a39e92..8c125e3f0 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sr.xaml @@ -12,6 +12,7 @@ Comma (,) Dot (.) Max. decimal places + Show thousands separator in results Copy failed, please try later Show error message when calculation fails diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/tr.xaml index aec5bec43..16fd93d9f 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/tr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/tr.xaml @@ -12,6 +12,7 @@ Virgül (,) Nokta (.) Maks. ondalık basamak + Sonuçlarda binlik ayırıcıyı göster Kopyalama başarısız oldu, lütfen daha sonra deneyin Hesaplama başarısız olduğunda hata mesajı göster diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/uk-UA.xaml index c2af4bbe3..c02fa6d46 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/uk-UA.xaml @@ -12,6 +12,7 @@ Кома (,) Крапка (.) Макс. кількість знаків після коми + Show thousands separator in results Копіювання не вдалося, спробуйте пізніше Показувати повідомлення про помилку, якщо обчислення не вдалося diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/vi.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/vi.xaml index 6efbda3e4..96d6549d6 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/vi.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/vi.xaml @@ -12,6 +12,7 @@ Dấu phẩy (,) dấu chấm (.) Tối đa. chữ số thập phân + Show thousands separator in results Copy failed, please try later Show error message when calculation fails diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-cn.xaml index 44a126e61..c0b8372e8 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-cn.xaml @@ -12,6 +12,7 @@ 逗号(,) 点(.) 小数点后最大位数 + 在结果中显示千分隔符 复制失败,请稍后再试 计算错误时显示错误消息 diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-tw.xaml index 0cf66c1cb..19cb2f525 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-tw.xaml @@ -12,6 +12,7 @@ 逗號 (,) 點 (.) 小數點後最大位數 + 在結果中顯示千位分隔符 複製失敗,請稍後再試 計算失敗時顯示錯誤訊息 diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs index a20a1ad5d..1b9a38e18 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs @@ -363,7 +363,7 @@ namespace Flow.Launcher.Plugin.Calculator string integerPart = parts[0]; string fractionalPart = parts.Length > 1 ? parts[1] : string.Empty; - if (integerPart.Length > 3) + if (_settings.UseThousandsSeparator && integerPart.Length > 3) { integerPart = ThousandGroupRegex.Replace(integerPart, groupSeparator); } diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Settings.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Settings.cs index 1544dc41f..69cd17ed2 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Settings.cs @@ -7,4 +7,6 @@ public class Settings public int MaxDecimalPlaces { get; set; } = 10; public bool ShowErrorMessage { get; set; } = false; + + public bool UseThousandsSeparator { get; set; } = true; } diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml index 9e7549b2d..82f8eec60 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml @@ -16,6 +16,7 @@ + @@ -66,6 +67,16 @@ Margin="{StaticResource SettingPanelItemTopBottomMargin}" HorizontalAlignment="Left" VerticalAlignment="Center" + Content="{DynamicResource flowlauncher_plugin_calculator_use_thousands_separator}" + IsChecked="{Binding Settings.UseThousandsSeparator, Mode=TwoWay}" /> + + diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ar.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ar.xaml index 608fe88a1..1edc1f2aa 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ar.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ar.xaml @@ -56,6 +56,8 @@ بحث في محتوى الملفات: بحث مفهرس: وصول سريع: + Folder Search: + File Search: الكلمة المفتاحية الحالية للإجراء تم مفعّل diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/cs.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/cs.xaml index 2381d501b..3fd1b721a 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/cs.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/cs.xaml @@ -56,6 +56,8 @@ Vyhledávání obsahu souborů: Vyhledávání v indexu: Rychlý přístup: + Folder Search: + File Search: Aktuální aktivační příkaz Hotovo Povoleno diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/da.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/da.xaml index f5f13e5a3..59b1216e7 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/da.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/da.xaml @@ -56,6 +56,8 @@ File Content Search: Index Search: Quick Access: + Folder Search: + File Search: Current Action Keyword Færdig Enabled diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/de.xaml index 8e352e614..3292568a6 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/de.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/de.xaml @@ -56,6 +56,8 @@ Suche nach Dateiinhalten: Index-Suche: Schnellzugriff: + Folder Search: + File Search: Aktuelles Aktions-Schlüsselwort Fertig Aktiviert diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml index c40040df5..45c0d72ac 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml @@ -58,6 +58,8 @@ File Content Search: Index Search: Quick Access: + Folder Search: + File Search: Current Action Keyword Done Enabled diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es-419.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es-419.xaml index 7379571a7..dc8860bd7 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es-419.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es-419.xaml @@ -56,6 +56,8 @@ File Content Search: Index Search: Quick Access: + Folder Search: + File Search: Current Action Keyword Hecho Enabled diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es.xaml index b59ada7ea..e623ac9c5 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es.xaml @@ -56,6 +56,8 @@ Búsqueda de contenido de archivo: Buscar índice: Acceso rápido: + Buscar carpeta: + Buscar archivo: Palabra clave de acción actual Aceptar Activado diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/fr.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/fr.xaml index b0b714154..5a91d3648 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/fr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/fr.xaml @@ -56,6 +56,8 @@ Recherche de contenu de fichier : Recherche dans l'index : Accès rapide : + Recherche de dossier : + Recherche de fichier : Mot-clé de l'action en cours Terminer Activé diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/he.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/he.xaml index a84d7707d..a5d4c3829 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/he.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/he.xaml @@ -56,6 +56,8 @@ חיפוש תוכן קובץ: חיפוש אינדקס: גישה מהירה: + Folder Search: + File Search: מילת פעולה נוכחית בוצע מופעל diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/it.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/it.xaml index a88ad2da1..ca774ce80 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/it.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/it.xaml @@ -56,6 +56,8 @@ Ricerca Contenuto File: Ricerca in Indice: Accesso Rapido: + Folder Search: + File Search: Parola Chiave Corrente Conferma Abilitato diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ja.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ja.xaml index b9a825bf2..34b9095cd 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ja.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ja.xaml @@ -56,6 +56,8 @@ File Content Search: Index Search: Quick Access: + Folder Search: + File Search: Current Action Keyword 完了 Enabled diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ko.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ko.xaml index e437926c8..0cbf40621 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ko.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ko.xaml @@ -56,6 +56,8 @@ 파일 내용 검색: 색인 검색: 빠른 접근: + Folder Search: + File Search: 현재 액션 키워드 완료 diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nb.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nb.xaml index b0672d3ad..9e96011b3 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nb.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nb.xaml @@ -56,6 +56,8 @@ Søk etter filinnhold: Indekssøk: Hurtigtilgang: + Folder Search: + File Search: Nåværende nøkkelord for handling Utført Aktivert diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nl.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nl.xaml index 3de4ce5e5..796bd0a97 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nl.xaml @@ -56,6 +56,8 @@ File Content Search: Index Search: Quick Access: + Folder Search: + File Search: Current Action Keyword Klaar Enabled diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pl.xaml index 1a01e8b90..11c3b83b6 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pl.xaml @@ -56,6 +56,8 @@ Przeszukiwanie zawartości plików: Wyszukiwanie indeksowe: Szybki dostęp: + Folder Search: + File Search: Bieżące słowo kluczowe akcji Zapisz Aktywny diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-br.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-br.xaml index 9a3a4ffeb..360342aed 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-br.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-br.xaml @@ -56,6 +56,8 @@ File Content Search: Index Search: Quick Access: + Folder Search: + File Search: Current Action Keyword Finalizado Enabled diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-pt.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-pt.xaml index 2d33768f9..82990de3c 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-pt.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-pt.xaml @@ -56,6 +56,8 @@ Pesquisa no conteúdo dos ficheiros: Pesquisa no índice: Acesso rápido: + Pesquisar pasta: + Pesquisar ficheiro: Palavra-chave atual OK Ativo diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml index cb28fcfd5..08d28d76f 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml @@ -56,6 +56,8 @@ File Content Search: Index Search: Quick Access: + Folder Search: + File Search: Current Action Keyword Подтвердить Включено diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sk.xaml index 1350969b6..1fc487585 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sk.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sk.xaml @@ -56,6 +56,8 @@ Vyhľadávanie obsahu súborov: Vyhľadávanie v indexe: Rýchly prístup: + Vyhľadávanie priečinkov: + Vyhľadávanie súborov: Aktuálny aktivačný príkaz Hotovo Povolené diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sr-Cyrl-RS.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sr-Cyrl-RS.xaml index e7979f6dd..e9c2f6f84 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sr-Cyrl-RS.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sr-Cyrl-RS.xaml @@ -56,6 +56,8 @@ File Content Search: Index Search: Quick Access: + Folder Search: + File Search: Current Action Keyword Done Enabled diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sr.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sr.xaml index ef7e6a5c3..00bf880a6 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sr.xaml @@ -56,6 +56,8 @@ File Content Search: Index Search: Quick Access: + Folder Search: + File Search: Current Action Keyword Gotovo Enabled diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/tr.xaml index 92461291b..f485c9f56 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/tr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/tr.xaml @@ -56,6 +56,8 @@ Dosya İçeriğini Ara: Dizin Araması: Hızlı Erişim: + Klasör Araması: + Dosya Araması: Geçerli Anahtar Kelime Tamam Etkin diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/uk-UA.xaml index 7600757a2..a6536afb4 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/uk-UA.xaml @@ -56,6 +56,8 @@ Пошук вмісту файлу: Індексний пошук: Швидкий доступ: + Пошук теки: + Пошук файлу: Поточне ключове слово дії Готово Увімкнено diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/vi.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/vi.xaml index 6416fc447..3b960522d 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/vi.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/vi.xaml @@ -56,6 +56,8 @@ Tìm kiếm nội dung tệp: Tìm kiếm chỉ mục: Truy cập nhanh + Folder Search: + File Search: Từ hành động hiện tại Xong Đã bật diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-cn.xaml index bcef2b668..9758062b3 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-cn.xaml @@ -56,6 +56,8 @@ 文件内容搜索: 索引搜索: 快速访问: + 目录搜索: + 文件搜索: 当前触发关键字 确认 已启用 diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-tw.xaml index d64cd7698..11b5bb2fa 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-tw.xaml @@ -2,7 +2,7 @@ - Please make a selection first + 請先選擇一或多個選項 Please select a folder path. Please choose a different name or folder path. Are you sure you want to delete this quick access link? @@ -13,12 +13,12 @@ Are you sure you want to permanently delete this file/folder? 成功刪除 已成功刪除 {0} - Assigning the global action keyword could bring up too many results during search. Please choose a specific action keyword - Quick Access can not be set to the global action keyword when enabled. Please choose a specific action keyword - The required service for Windows Index Search does not appear to be running - To fix this, start the Windows Search service. Select here to remove this warning - The warning message has been switched off. As an alternative for searching files and folders, would you like to install Everything plugin?{0}{0}Select 'Yes' to install Everything plugin, or 'No' to return - Explorer Alternative + 設定全局操作關鍵字可能會導致搜尋結果過多。請選擇一個特定的操作關鍵字 + 啟用「快速存取」後,無法將其設定為全域操作關鍵字。請選擇一個特定的操作關鍵字 + Windows 索引搜尋所需的服務似乎未運作 + 若要解決此問題,請啟動 Windows 搜尋服務。選擇此處可移除此警告 + 警告訊息已關閉。要使用 Everything 外掛程式搜尋檔案和資料夾,您是否願意安裝該外掛程式? {0}{0}選擇「是」安裝 Everything 插件,或選擇「否」返回 + 瀏覽器替代方案 Error occurred during search: {0} Could not open folder Could not open file @@ -29,7 +29,7 @@ 編輯 新增 General Setting - Customise Action Keywords + 更改觸發關鍵字 Customise Quick Access 快速訪問連結 Everything Setting @@ -45,7 +45,7 @@ Launch Hidden 編輯器路 Shell Path - Index Search Excluded Paths + 搜尋索引排除路徑 使用程式所在目錄作為工作目錄 Display more information like size and age in tooltips Hit Enter to open folder in Default File Manager @@ -53,13 +53,15 @@ 索引選項 搜尋: 路徑搜尋: - File Content Search: - Index Search: + 檔案內容搜尋: + 索引搜尋: 快速存取: - Current Action Keyword + Folder Search: + File Search: + 目前觸發關鍵字 已啟用 - When disabled Flow will not execute this search option, and will additionally revert back to '*' to free up the action keyword + 停用此選項後,Flow 將不會執行此搜尋選項,並會還原為「*」以釋放操作關鍵字 Everything Windows Index Direct Enumeration @@ -105,26 +107,26 @@ 檔案 資料夾 刪除所選內容 - Run as different user - Run the selected using a different user account + 以另一個使用者的身分執行 + 使用其他帳戶運行所選的程式 開啟檔案位置 Open the location that contains current item 在編輯器中開啟: Failed to open file at {0} with Editor {1} at {2} Open With Shell: Failed to open folder {0} with Shell {1} at {2} - Exclude current and sub-directories from Index Search - Excluded from Index Search - Open Windows Indexing Options - Manage indexed files and folders - Failed to open Windows Indexing Options - Add to Quick Access + 從索引搜尋中排除目前目錄及其子目錄 + 已從索引搜尋中排除 + 開啟Windows搜尋索引功能的設定 + 管理索引的檔案和資料夾 + 無法開啟Windows索引服務的設定 + 加入快速存取 Add current item to Quick Access 添加成功 - Successfully added to Quick Access - Successfully Removed - Successfully removed from Quick Access - Add to Quick Access so it can be opened with Explorer's Search Activation action keyword + 已成功添加到快速存取 + 成功移除 + 成功從快速存取中移除 + 添加到快速存取,以便可以使用檔案總管的搜尋啟動操作關鍵字打開它 從快速訪問中移除 從快速訪問中移除 Remove current item from Quick Access diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index d7b069082..9420e3d3a 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -1,13 +1,14 @@ -using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; -using Flow.Launcher.Plugin.Explorer.Search.Everything; -using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks; -using Flow.Launcher.Plugin.SharedCommands; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Flow.Launcher.Plugin.Explorer.Exceptions; +using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; +using Flow.Launcher.Plugin.Explorer.Search.Everything; +using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks; +using Flow.Launcher.Plugin.SharedCommands; +using static Flow.Launcher.Plugin.Explorer.Settings; using Path = System.IO.Path; namespace Flow.Launcher.Plugin.Explorer.Search @@ -18,6 +19,14 @@ namespace Flow.Launcher.Plugin.Explorer.Search internal Settings Settings; + private readonly Dictionary _allowedTypesByActionKeyword = new() + { + { ActionKeyword.FileSearchActionKeyword, [ResultType.File] }, + { ActionKeyword.FolderSearchActionKeyword, [ResultType.Folder, ResultType.Volume] }, + { ActionKeyword.SearchActionKeyword, [ResultType.File, ResultType.Folder, ResultType.Volume] }, + }; + + public SearchManager(Settings settings, PluginInitContext context) { Context = context; @@ -44,48 +53,71 @@ namespace Flow.Launcher.Plugin.Explorer.Search } } + /// + /// Results for the different types of searches as follows: + /// 1. Search, only include results from: + /// - Files + /// - Folders + /// - Quick Access Links + /// - Path navigation + /// 2. File Content Search, only include results from: + /// - File contents from index search engines i.e. Windows Index, Everything (may not be available due its beta version) + /// 3. Path Search, only include results from: + /// - Path navigation + /// 4. Quick Access Links, only include results from: + /// - Full list of Quick Access Links if query is empty + /// - Matched Quick Access Links if query is not empty + /// - Quick Access Links that are matched on path, e.g. query "window" for results that contain 'window' in the path (even if not in the title), + /// i.e. result with path c:\windows\system32 + /// 5. Folder Search, only include results from: + /// - Folders + /// - Quick Access Links + /// 6. File Search, only include results from: + /// - Files + /// - Quick Access Links + /// internal async Task> SearchAsync(Query query, CancellationToken token) { var results = new HashSet(PathEqualityComparator.Instance); - // This allows the user to type the below action keywords and see/search the list of quick folder links - if (ActionKeywordMatch(query, Settings.ActionKeyword.SearchActionKeyword) - || ActionKeywordMatch(query, Settings.ActionKeyword.QuickAccessActionKeyword) - || ActionKeywordMatch(query, Settings.ActionKeyword.PathSearchActionKeyword) - || ActionKeywordMatch(query, Settings.ActionKeyword.IndexSearchActionKeyword) - || ActionKeywordMatch(query, Settings.ActionKeyword.FileContentSearchActionKeyword)) + var keyword = query.ActionKeyword.Length == 0 ? Query.GlobalPluginWildcardSign : query.ActionKeyword; + + // No action keyword matched - plugin should not handle this query, return empty results. + var activeActionKeywords = Settings.GetActiveActionKeywords(keyword); + if (activeActionKeywords.Count == 0) { - if (string.IsNullOrEmpty(query.Search) && ActionKeywordMatch(query, Settings.ActionKeyword.QuickAccessActionKeyword)) - return QuickAccess.AccessLinkListAll(query, Settings.QuickAccessLinks); - } - else - { - // No action keyword matched- plugin should not handle this query, return empty results. - return new List(); + return [.. results]; } - IAsyncEnumerable searchResults; + var queryIsEmpty = string.IsNullOrEmpty(query.Search); + if (queryIsEmpty && activeActionKeywords.ContainsKey(ActionKeyword.QuickAccessActionKeyword)) + { + return QuickAccess.AccessLinkListAll(query, Settings.QuickAccessLinks); + } - bool isPathSearch = query.Search.IsLocationPathString() + if (queryIsEmpty) + { + return [.. results]; + } + + var isPathSearch = query.Search.IsLocationPathString() || EnvironmentVariables.IsEnvironmentVariableSearch(query.Search) || EnvironmentVariables.HasEnvironmentVar(query.Search); + IAsyncEnumerable searchResults; + string engineName; switch (isPathSearch) { case true - when ActionKeywordMatch(query, Settings.ActionKeyword.PathSearchActionKeyword) - || ActionKeywordMatch(query, Settings.ActionKeyword.SearchActionKeyword): - + when CanUsePathSearchByActionKeywords(activeActionKeywords): results.UnionWith(await PathSearchAsync(query, token).ConfigureAwait(false)); - - return results.ToList(); + return [.. results]; case false - when ActionKeywordMatch(query, Settings.ActionKeyword.FileContentSearchActionKeyword): - // Intentionally require enabling of Everything's content search due to its slowness + when activeActionKeywords.ContainsKey(ActionKeyword.FileContentSearchActionKeyword): if (Settings.ContentIndexProvider is EverythingSearchManager && !Settings.EnableEverythingContentSearch) return EverythingContentSearchResult(query); @@ -93,37 +125,41 @@ namespace Flow.Launcher.Plugin.Explorer.Search engineName = Enum.GetName(Settings.ContentSearchEngine); break; - case false - when ActionKeywordMatch(query, Settings.ActionKeyword.IndexSearchActionKeyword) - || ActionKeywordMatch(query, Settings.ActionKeyword.SearchActionKeyword): + case true or false + when activeActionKeywords.ContainsKey(ActionKeyword.QuickAccessActionKeyword): + return QuickAccess.AccessLinkListMatched(query, Settings.QuickAccessLinks); + + case false + when CanUseIndexSearchByActionKeywords(activeActionKeywords): searchResults = Settings.IndexProvider.SearchAsync(query.Search, token); engineName = Enum.GetName(Settings.IndexSearchEngine); break; - - case true or false - when ActionKeywordMatch(query, Settings.ActionKeyword.QuickAccessActionKeyword): - return QuickAccess.AccessLinkListMatched(query, Settings.QuickAccessLinks); - default: - return results.ToList(); + return [.. results]; + } - // Merge Quick Access Link results for non-path searches. - results.UnionWith(QuickAccess.AccessLinkListMatched(query, Settings.QuickAccessLinks)); + var actions = activeActionKeywords.Keys.ToList(); + //Merge Quick Access Link results for non-path searches. + results.UnionWith(GetQuickAccessResultsFilteredByActionKeyword(query, actions)); try { await foreach (var search in searchResults.WithCancellation(token).ConfigureAwait(false)) - if (search.Type == ResultType.File && IsExcludedFile(search)) { + { + if (search.Type == ResultType.File && IsExcludedFile(search)) continue; - } else { - results.Add(ResultManager.CreateResult(query, search)); - } + + if (IsResultTypeFilteredByActionKeyword(search.Type, actions)) + continue; + + results.Add(ResultManager.CreateResult(query, search)); + } } catch (OperationCanceledException) { - return new List(); + return [.. results]; } catch (EngineNotAvailableException) { @@ -137,33 +173,13 @@ namespace Flow.Launcher.Plugin.Explorer.Search results.RemoveWhere(r => Settings.IndexSearchExcludedSubdirectoryPaths.Any( excludedPath => FilesFolders.PathContains(excludedPath.Path, r.SubTitle, allowEqual: true))); - return results.ToList(); - } - - private bool ActionKeywordMatch(Query query, Settings.ActionKeyword allowedActionKeyword) - { - var keyword = query.ActionKeyword.Length == 0 ? Query.GlobalPluginWildcardSign : query.ActionKeyword; - - return allowedActionKeyword switch - { - Settings.ActionKeyword.SearchActionKeyword => Settings.SearchActionKeywordEnabled && - keyword == Settings.SearchActionKeyword, - Settings.ActionKeyword.PathSearchActionKeyword => Settings.PathSearchKeywordEnabled && - keyword == Settings.PathSearchActionKeyword, - Settings.ActionKeyword.FileContentSearchActionKeyword => Settings.FileContentSearchKeywordEnabled && - keyword == Settings.FileContentSearchActionKeyword, - Settings.ActionKeyword.IndexSearchActionKeyword => Settings.IndexSearchKeywordEnabled && - keyword == Settings.IndexSearchActionKeyword, - Settings.ActionKeyword.QuickAccessActionKeyword => Settings.QuickAccessKeywordEnabled && - keyword == Settings.QuickAccessActionKeyword, - _ => throw new ArgumentOutOfRangeException(nameof(allowedActionKeyword), allowedActionKeyword, "actionKeyword out of range") - }; + return [.. results]; } private List EverythingContentSearchResult(Query query) { - return new List() - { + return + [ new() { Title = Localize.flowlauncher_plugin_everything_enable_content_search(), @@ -172,11 +188,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search Action = c => { Settings.EnableEverythingContentSearch = true; - Context.API.ChangeQuery(query.RawQuery, true); + Context.API.ChangeQuery(query.TrimmedQuery, true); return false; } } - }; + ]; } private async Task> PathSearchAsync(Query query, CancellationToken token = default) @@ -197,7 +213,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search // Check that actual location exists, otherwise directory search will throw directory not found exception if (!FilesFolders.ReturnPreviousDirectoryIfIncompleteString(path).LocationExists()) - return results.ToList(); + return [.. results]; var useIndexSearch = Settings.IndexSearchEngine is Settings.IndexSearchEngineOption.WindowsIndex && UseWindowsIndexForDirectorySearch(path); @@ -209,7 +225,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search : ResultManager.CreateOpenCurrentFolderResult(retrievedDirectoryPath, query.ActionKeyword, useIndexSearch)); if (token.IsCancellationRequested) - return new List(); + return [.. results]; IAsyncEnumerable directoryResult; @@ -231,7 +247,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search } if (token.IsCancellationRequested) - return new List(); + return [.. results]; try { @@ -246,14 +262,14 @@ namespace Flow.Launcher.Plugin.Explorer.Search } - return results.ToList(); + return [.. results]; } public bool IsFileContentSearch(string actionKeyword) => actionKeyword == Settings.FileContentSearchActionKeyword; public static bool UseIndexSearch(string path) { - if (Main.Settings.IndexSearchEngine is not Settings.IndexSearchEngineOption.WindowsIndex) + if (Main.Settings.IndexSearchEngine is not IndexSearchEngineOption.WindowsIndex) return false; // Check if the path is using windows index search @@ -275,10 +291,67 @@ namespace Flow.Launcher.Plugin.Explorer.Search private bool IsExcludedFile(SearchResult result) { - string[] excludedFileTypes = Settings.ExcludedFileTypes.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + string[] excludedFileTypes = Settings.ExcludedFileTypes.Split([','], StringSplitOptions.RemoveEmptyEntries); string fileExtension = Path.GetExtension(result.FullPath).TrimStart('.'); return excludedFileTypes.Contains(fileExtension, StringComparer.OrdinalIgnoreCase); } + + private List GetQuickAccessResultsFilteredByActionKeyword(Query query, List actions) + { + if (!Settings.QuickAccessKeywordEnabled) + return []; + + var results = QuickAccess.AccessLinkListMatched(query, Settings.QuickAccessLinks); + if (results.Count == 0) + return []; + + return results + .Where(r => r.ContextData is SearchResult result + && !IsResultTypeFilteredByActionKeyword(result.Type, actions)) + .ToList(); + } + private bool IsResultTypeFilteredByActionKeyword(ResultType type, List actions) + { + var actionsWithWhitelist = actions.Intersect(_allowedTypesByActionKeyword.Keys).ToList(); + if (actionsWithWhitelist.Count == 0) return false; + + // Check if ANY active keyword allows this type (union behavior) + foreach (var action in actionsWithWhitelist) + { + if (_allowedTypesByActionKeyword.TryGetValue(action, out var allowedTypes)) + { + if (allowedTypes.Contains(type)) + return false; + } + } + + return true; + } + + private bool CanUseIndexSearchByActionKeywords(Dictionary actions) + { + var keysToUseIndexSearch = new[] + { + ActionKeyword.FileSearchActionKeyword, ActionKeyword.FolderSearchActionKeyword, + ActionKeyword.IndexSearchActionKeyword, ActionKeyword.SearchActionKeyword + }; + + return keysToUseIndexSearch.Any(actions.ContainsKey); + } + + // Action keywords that supports patch search in results. + private bool CanUsePathSearchByActionKeywords(Dictionary actions) + { + var keysThatSupportPathSearch = new[] + { + ActionKeyword.PathSearchActionKeyword, + ActionKeyword.SearchActionKeyword, + }; + + return keysThatSupportPathSearch.Any(actions.ContainsKey); + + } + } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs index 8d62531cd..e65c03e9b 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs @@ -1,12 +1,13 @@ -using Flow.Launcher.Plugin.Explorer.Search; -using Flow.Launcher.Plugin.Explorer.Search.Everything; -using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks; -using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; -using System; +using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Text.Json.Serialization; +using Flow.Launcher.Plugin.Explorer.Search; +using Flow.Launcher.Plugin.Explorer.Search.Everything; using Flow.Launcher.Plugin.Explorer.Search.IProvider; +using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks; +using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; namespace Flow.Launcher.Plugin.Explorer { @@ -58,6 +59,15 @@ namespace Flow.Launcher.Plugin.Explorer public bool QuickAccessKeywordEnabled { get; set; } + + public string FolderSearchActionKeyword { get; set; } = Query.GlobalPluginWildcardSign; + + public bool FolderSearchKeywordEnabled { get; set; } + + public string FileSearchActionKeyword { get; set; } = Query.GlobalPluginWildcardSign; + + public bool FileSearchKeywordEnabled { get; set; } + public bool WarnWindowsSearchServiceOff { get; set; } = true; public bool ShowFileSizeInPreviewPanel { get; set; } = true; @@ -160,7 +170,9 @@ namespace Flow.Launcher.Plugin.Explorer PathSearchActionKeyword, FileContentSearchActionKeyword, IndexSearchActionKeyword, - QuickAccessActionKeyword + QuickAccessActionKeyword, + FolderSearchActionKeyword, + FileSearchActionKeyword, } internal string GetActionKeyword(ActionKeyword actionKeyword) => actionKeyword switch @@ -170,6 +182,8 @@ namespace Flow.Launcher.Plugin.Explorer ActionKeyword.FileContentSearchActionKeyword => FileContentSearchActionKeyword, ActionKeyword.IndexSearchActionKeyword => IndexSearchActionKeyword, ActionKeyword.QuickAccessActionKeyword => QuickAccessActionKeyword, + ActionKeyword.FolderSearchActionKeyword => FolderSearchActionKeyword, + ActionKeyword.FileSearchActionKeyword => FileSearchActionKeyword, _ => throw new ArgumentOutOfRangeException(nameof(actionKeyword), actionKeyword, "ActionKeyWord property not found") }; @@ -180,6 +194,8 @@ namespace Flow.Launcher.Plugin.Explorer ActionKeyword.FileContentSearchActionKeyword => FileContentSearchActionKeyword = keyword, ActionKeyword.IndexSearchActionKeyword => IndexSearchActionKeyword = keyword, ActionKeyword.QuickAccessActionKeyword => QuickAccessActionKeyword = keyword, + ActionKeyword.FolderSearchActionKeyword => FolderSearchActionKeyword = keyword, + ActionKeyword.FileSearchActionKeyword => FileSearchActionKeyword = keyword, _ => throw new ArgumentOutOfRangeException(nameof(actionKeyword), actionKeyword, "ActionKeyWord property not found") }; @@ -190,6 +206,8 @@ namespace Flow.Launcher.Plugin.Explorer ActionKeyword.IndexSearchActionKeyword => IndexSearchKeywordEnabled, ActionKeyword.FileContentSearchActionKeyword => FileContentSearchKeywordEnabled, ActionKeyword.QuickAccessActionKeyword => QuickAccessKeywordEnabled, + ActionKeyword.FolderSearchActionKeyword => FolderSearchKeywordEnabled, + ActionKeyword.FileSearchActionKeyword => FileSearchKeywordEnabled, _ => throw new ArgumentOutOfRangeException(nameof(actionKeyword), actionKeyword, "ActionKeyword enabled status not defined") }; @@ -200,7 +218,27 @@ namespace Flow.Launcher.Plugin.Explorer ActionKeyword.IndexSearchActionKeyword => IndexSearchKeywordEnabled = enable, ActionKeyword.FileContentSearchActionKeyword => FileContentSearchKeywordEnabled = enable, ActionKeyword.QuickAccessActionKeyword => QuickAccessKeywordEnabled = enable, + ActionKeyword.FolderSearchActionKeyword => FolderSearchKeywordEnabled = enable, + ActionKeyword.FileSearchActionKeyword => FileSearchKeywordEnabled = enable, _ => throw new ArgumentOutOfRangeException(nameof(actionKeyword), actionKeyword, "ActionKeyword enabled status not defined") }; + + // Returns a dictionary because some ActionKeywords may use wildcards (*), + // which means multiple ActionKeywords can be considered active at the same time. + // Using a dictionary ensures O(1) lookup time when checking which actions + // are enabled. + internal Dictionary GetActiveActionKeywords(string actionKeywordStr) + { + var result = new Dictionary(); + if (string.IsNullOrEmpty(actionKeywordStr)) return null; + foreach (var action in Enum.GetValues()) + { + var keywordStr = GetActionKeyword(action); + if (string.IsNullOrEmpty(keywordStr)) continue; + var isEnabled = GetActionKeywordEnabled(action); + if (keywordStr == actionKeywordStr && isEnabled) result.Add(action, keywordStr); + } + return result; + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs index 28740b35b..0276b7845 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs @@ -279,7 +279,11 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels new(Settings.ActionKeyword.IndexSearchActionKeyword, "plugin_explorer_actionkeywordview_indexsearch"), new(Settings.ActionKeyword.QuickAccessActionKeyword, - "plugin_explorer_actionkeywordview_quickaccess") + "plugin_explorer_actionkeywordview_quickaccess"), + new(Settings.ActionKeyword.FolderSearchActionKeyword, + "plugin_explorer_actionkeywordview_foldersearch"), + new(Settings.ActionKeyword.FileSearchActionKeyword, + "plugin_explorer_actionkeywordview_filesearch") }; } @@ -529,7 +533,7 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels [RelayCommand] private void OpenShellPath() { - var path = PromptUserSelectPath(ResultType.File, Settings.EditorPath != null ? Path.GetDirectoryName(Settings.EditorPath) : null); + var path = PromptUserSelectPath(ResultType.File, Settings.ShellPath != null ? Path.GetDirectoryName(Settings.ShellPath) : null); if (path is null) return; diff --git a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Languages/zh-tw.xaml index 0c6477a34..4a03db22f 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Languages/zh-tw.xaml @@ -1,7 +1,7 @@  - Activate {0} plugin action keyword + 啟動 {0} 外掛程式操作關鍵字 套件關鍵字提示 提供套件關鍵字搜尋提示 diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml index c13048133..449614e96 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-tw.xaml @@ -5,9 +5,9 @@ 正在下載擴充功能 下載完成 錯誤:無法下載擴充功能 - {0} by {1} {2}{3}Would you like to uninstall this plugin? After the uninstallation Flow will automatically restart. + {0}(來自 {1} {2} {3})您想解除安裝此外掛程式嗎?卸載後,Flow 將自動重新啟動。 {0} by {1} {2}{2}Would you like to uninstall this plugin? - {0} by {1} {2}{3}Would you like to install this plugin? After the installation Flow will automatically restart. + {0}(來自 {1} {2} {3})您想安裝此外掛程式嗎?安裝後,Flow 將自動重新啟動。 {0} by {1} {2}{2}Would you like to install this plugin? 安裝擴充功能 Installing Plugin @@ -16,27 +16,27 @@ Keep plugin settings Do you want to keep the settings of the plugin for the next usage? 外掛安裝成功。正在重啟 Flow,請稍後... - Unable to find the plugin.json metadata file from the extracted zip file. - Error: A plugin which has the same or greater version with {0} already exists. + 無法從解壓縮後的zip檔案中找到plugin.json檔案。 + 錯誤:已經安裝版本高於 {0} 的外掛程式。 安裝擴充功能時發生錯誤 嘗試安裝 {0} 時發生錯誤 Error uninstalling plugin 無可用更新 所有插件均為最新版本 - {0} by {1} {2}{3}Would you like to update this plugin? After the update Flow will automatically restart. + {0}(來自 {1} {2} {3})您想更新此外掛程式嗎?更新後,Flow 將自動重新啟動。 {0} by {1} {2}{2}Would you like to update this plugin? 擴充功能更新 已安裝此擴充功能 - Plugin Manifest Download Failed - Please check if you can connect to github.com. This error means you may not be able to install or update plugins. + 外掛程式清單下載失敗 + 請檢查您是否可以連接到github.com。此錯誤表示您可能無法安裝或更新外掛程式。 Update all plugins Would you like to update all plugins? 你要更新{0}個插件?{1}Flow Launcher會在更新所有插件後重新啟動。 Would you like to update {0} plugins? {0} plugins successfully updated. Restarting Flow, please wait... Plugin {0} successfully updated. Restarting Flow, please wait... - Installing from an unknown source - You are installing this plugin from an unknown source and it may contain potential risks!{0}{0}Please ensure you understand where this plugin is from and that it is safe.{0}{0}Would you like to continue still?{0}{0}(You can switch off this warning via settings) + 從未知來源安裝 + 您正在安裝來自未知來源的插件,它可能有潛在風險!{0}{0}請確保您了解此插件的來源並確認其安全性。{0}{0}是否繼續? {0}{0}(您可以在設定中關閉此警告) Plugin {0} successfully installed. Please restart Flow. Plugin {0} successfully uninstalled. Please restart Flow. @@ -59,12 +59,12 @@ 查看擴充功能的網站 查看原始碼 查看擴充功能的原始碼 - Suggest an enhancement or submit an issue - Suggest an enhancement or submit an issue to the plugin developer - Go to Flow's plugins repository - Visit the PluginsManifest repository to see community-made plugin submissions + 提出改進建議或提交問題 + 向作者提出改進建議或提交問題 + 前往 Flow 的線上外掛清單 + 造訪PluginsManifest儲存庫,查看社群提交的插件 - Install from unknown source warning + 安裝未知來源警告 以插件管理員安裝/移除/更新插件後自動重新啟動Flow Launcher diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj index 39586771f..ec8a32b95 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj @@ -54,7 +54,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/zh-tw.xaml index 0a7176d2c..086ffc080 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Languages/zh-tw.xaml @@ -1,14 +1,14 @@  - Process Killer - Kill running processes from Flow Launcher + 進程結束器 + 使用Flow Launcher終止正在執行的進程 - kill all instances of "{0}" - kill {0} processes - kill all instances + 刪除{0}的所有實例 + 終止 {0} 個進程 + 終止所有實例 - Show title for processes with visible windows - Put processes with visible windows on the top + 顯示具有可見視窗的進程的標題 + 將可見的進程置於最上方 diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-tw.xaml index 2653b69da..df2714022 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-tw.xaml @@ -14,7 +14,7 @@ 已啟用 Disabled 路徑 - All Programs + 所有程式 File Type 重新建立索引 正在建立索引 @@ -23,16 +23,16 @@ UWP Apps When enabled, Flow will load UWP Applications Start Menu - When enabled, Flow will load programs from the start menu + 啟用後,Flow將從開始功能表載入程式 Registry - When enabled, Flow will load programs from the registry + 啟用後,Flow將從註冊表載入程式 PATH When enabled, Flow will load programs from the PATH environment variable - Hide app path - For executable files such as UWP or lnk, hide the file path from being visible + 隱藏程式路徑 + 對於UWP或lnk等可執行文件,隱藏檔案路徑 Hide uninstallers Hides programs with common uninstaller names, such as unins000.exe - Search in Program Description + 在程式說明中搜尋 Flow will search program's description Hide duplicated apps Hide duplicated Win32 programs that are already in the UWP list @@ -45,7 +45,7 @@ 最大搜尋深度(-1是無限的): 請先選擇一項 - Are you sure you want to delete the selected program sources? + 您確定要刪除選定的程式來源嗎? Please select program sources that are not added by you Please select program sources that are added by you Another program source with the same location already exists. @@ -73,7 +73,7 @@ Insert protocols of .url files you want to index. Protocols should be separated by ';', and should end with "://". (ex>ftp://;mailto://) - Run As Different User + 以另一個使用者的身分執行 以系統管理員身分執行 開啟檔案位置 Hide @@ -84,16 +84,16 @@ 無效的路徑 - Customized Explorer - Args + 自訂檔案管理器 + 參數 You can customize the explorer used for opening the container folder by inputing the Environmental Variable of the explorer you want to use. It will be useful to use CMD to test whether the Environmental Variable is available. - Enter the customized args you want to add for your customized explorer. %s for parent directory, %f for full path (which only works for win32). Check the explorer's website for details. + 輸入您要為自訂資源管理器新增的自訂參數。 %s 表示父目錄,%f 表示完整路徑(僅適用於 Win32)。請造訪資源管理器的網站以了解詳情。 Error - Successfully disabled this program from displaying in your query - This app is not intended to be run as administrator + 已經禁止此程式顯示在您的搜尋結果中 + 此應用程式不應以管理員身分執行 Unable to run {0} diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 456085fca..9c84747d2 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -334,7 +334,7 @@ namespace Flow.Launcher.Plugin.Program } } - public static async Task IndexWin32ProgramsAsync() + public static async Task IndexWin32ProgramsAsync(bool resetCache) { await _win32sLock.WaitAsync(); try @@ -345,7 +345,10 @@ namespace Flow.Launcher.Plugin.Program { _win32s.Add(win32); } - ResetCache(); + if (resetCache) + { + ResetCache(); + } await Context.API.SaveCacheBinaryStorageAsync>(Win32CacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath); lock (_lastIndexTimeLock) { @@ -362,7 +365,7 @@ namespace Flow.Launcher.Plugin.Program } } - public static async Task IndexUwpProgramsAsync() + public static async Task IndexUwpProgramsAsync(bool resetCache) { await _uwpsLock.WaitAsync(); try @@ -373,7 +376,10 @@ namespace Flow.Launcher.Plugin.Program { _uwps.Add(uwp); } - ResetCache(); + if (resetCache) + { + ResetCache(); + } await Context.API.SaveCacheBinaryStorageAsync>(UwpCacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath); lock (_lastIndexTimeLock) { @@ -394,12 +400,12 @@ namespace Flow.Launcher.Plugin.Program { var win32Task = Task.Run(async () => { - await Context.API.StopwatchLogInfoAsync(ClassName, "Win32Program index cost", IndexWin32ProgramsAsync); + await Context.API.StopwatchLogInfoAsync(ClassName, "Win32Program index cost", () => IndexWin32ProgramsAsync(resetCache: true)); }); var uwpTask = Task.Run(async () => { - await Context.API.StopwatchLogInfoAsync(ClassName, "UWPProgram index cost", IndexUwpProgramsAsync); + await Context.API.StopwatchLogInfoAsync(ClassName, "UWPProgram index cost", () => IndexUwpProgramsAsync(resetCache: true)); }); await Task.WhenAll(win32Task, uwpTask).ConfigureAwait(false); @@ -407,9 +413,21 @@ namespace Flow.Launcher.Plugin.Program internal static void ResetCache() { - var oldCache = cache; - cache = new MemoryCache(cacheOptions); - oldCache.Dispose(); + var newCache = new MemoryCache(cacheOptions); + + // Atomically swap and get the previous cache instance, avoids double-dispose/lost-assignment race + // where each caller receives a distinct prior instance to dispose. + var oldCache = Interlocked.Exchange(ref cache, newCache); + + // Dispose the previous instance (if any)- each caller gets a unique prior instance from above + try + { + oldCache?.Dispose(); + } + catch (Exception e) + { + Context.API.LogException(ClassName, "Failed to dispose old program cache", e); + } } public Control CreateSettingPanel() @@ -442,12 +460,26 @@ namespace Flow.Launcher.Plugin.Program Title = Context.API.GetTranslation("flowlauncher_plugin_program_disable_program"), Action = c => { - _ = DisableProgramAsync(program); - Context.API.ShowMsg( - Context.API.GetTranslation("flowlauncher_plugin_program_disable_dlgtitle_success"), - Context.API.GetTranslation( - "flowlauncher_plugin_program_disable_dlgtitle_success_message")); - Context.API.ReQuery(); + _ = Task.Run(async () => + { + try + { + var disabled = await DisableProgramAsync(program); + if (disabled) + { + ResetCache(); + Context.API.ShowMsg( + Context.API.GetTranslation("flowlauncher_plugin_program_disable_dlgtitle_success"), + Context.API.GetTranslation( + "flowlauncher_plugin_program_disable_dlgtitle_success_message")); + } + Context.API.ReQuery(); + } + catch (Exception e) + { + Context.API.LogException(ClassName, "Failed to disable program", e); + } + }); return false; }, IcoPath = "Images/disable.png", @@ -458,52 +490,50 @@ namespace Flow.Launcher.Plugin.Program return menuOptions; } - private static async Task DisableProgramAsync(IProgram programToDelete) + private static async Task DisableProgramAsync(IProgram programToDelete) { if (_settings.DisabledProgramSources.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier)) - return; + { + return false; + } await _uwpsLock.WaitAsync(); - var reindexUwps = true; try { - reindexUwps = _uwps.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier); - var program = _uwps.First(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier); - program.Enabled = false; - _settings.DisabledProgramSources.Add(new ProgramSource(program)); + var program = _uwps.FirstOrDefault(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier); + if (program != null) + { + program.Enabled = false; + _settings.DisabledProgramSources.Add(new ProgramSource(program)); + // Reindex UWP programs + _ = Task.Run(() => IndexUwpProgramsAsync(resetCache: false)); + return true; + } } finally { _uwpsLock.Release(); } - // Reindex UWP programs - if (reindexUwps) - { - _ = Task.Run(IndexUwpProgramsAsync); - return; - } - await _win32sLock.WaitAsync(); - var reindexWin32s = true; try { - reindexWin32s = _win32s.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier); - var program = _win32s.First(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier); - program.Enabled = false; - _settings.DisabledProgramSources.Add(new ProgramSource(program)); + var program = _win32s.FirstOrDefault(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier); + if (program != null) + { + program.Enabled = false; + _settings.DisabledProgramSources.Add(new ProgramSource(program)); + // Reindex Win32 programs + _ = Task.Run(() => IndexWin32ProgramsAsync(resetCache: false)); + return true; + } } finally { _win32sLock.Release(); } - // Reindex Win32 programs - if (reindexWin32s) - { - _ = Task.Run(IndexWin32ProgramsAsync); - return; - } + return false; } public static void StartProcess(Func runProcess, ProcessStartInfo info) diff --git a/Plugins/Flow.Launcher.Plugin.Program/NativeMethods.txt b/Plugins/Flow.Launcher.Plugin.Program/NativeMethods.txt index ecd547dff..a8fe178c7 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/NativeMethods.txt +++ b/Plugins/Flow.Launcher.Plugin.Program/NativeMethods.txt @@ -7,4 +7,7 @@ S_OK SLGP_FLAGS WIN32_FIND_DATAW SLR_FLAGS -IShellLinkW \ No newline at end of file +IShellItem +SHCreateItemFromParsingName +IShellLinkW +CoTaskMemFree \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLocalization.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLocalization.cs index 5c2f6f976..815b74b96 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLocalization.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLocalization.cs @@ -3,7 +3,6 @@ using System.IO; using System.Runtime.InteropServices; using Windows.Win32; using Windows.Win32.Foundation; -using Windows.Win32.System.LibraryLoader; namespace Flow.Launcher.Plugin.Program.Programs { @@ -21,46 +20,28 @@ namespace Flow.Launcher.Plugin.Program.Programs /// The localized name as string or . public static unsafe string GetLocalizedName(string path) { - const int capacity = 1024; - Span buffer = new char[capacity]; - - // If there is no resource to localize a file name the method returns a non zero value. - fixed (char* bufferPtr = buffer) + int retCode = PInvoke.SHCreateItemFromParsingName(path, null, typeof(Windows.Win32.UI.Shell.IShellItem).GUID, out object shellItemObj); + if (retCode != 0 || shellItemObj is not Windows.Win32.UI.Shell.IShellItem shellItem) { - int id; - fixed (char* pathPtr = path) - { - var result = PInvoke.SHGetLocalizedName(new PCWSTR(pathPtr), bufferPtr, capacity, &id); - - if (result != HRESULT.S_OK) - { - return string.Empty; - } - - var resourcePathStr = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(bufferPtr).ToString(); - fixed (char* resourcePathPtr = resourcePathStr) - { - _ = PInvoke.ExpandEnvironmentStrings(new PCWSTR(resourcePathPtr), bufferPtr, capacity); - using var handle = PInvoke.LoadLibraryEx(resourcePathStr, - LOAD_LIBRARY_FLAGS.DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_FLAGS.LOAD_LIBRARY_AS_DATAFILE); - if (handle.IsInvalid) - { - return string.Empty; - } - - // not sure about the behavior of Pinvoke.LoadString, so we clear the buffer before using it (so it must be a null-terminated string) - buffer.Clear(); - - if (PInvoke.LoadString(handle, (uint)id, buffer, capacity) != 0) - { - var lString = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(bufferPtr).ToString(); - return lString; - } - } - } + return string.Empty; } - return string.Empty; + try + { + PWSTR displayName; + shellItem.GetDisplayName(Windows.Win32.UI.Shell.SIGDN.SIGDN_NORMALDISPLAY, &displayName); + string filename = displayName.ToString(); + PInvoke.CoTaskMemFree(displayName); + return filename; + } + catch + { + return string.Empty; + } + finally + { + Marshal.ReleaseComObject(shellItem); + } } /// diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs index f5e4008ac..dd49a7053 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs @@ -289,7 +289,7 @@ namespace Flow.Launcher.Plugin.Program.Programs } private static readonly Channel PackageChangeChannel = Channel.CreateBounded(1); - private static PackageCatalog? catalog; + private static PackageCatalog catalog; public static async Task WatchPackageChangeAsync() { @@ -316,7 +316,7 @@ namespace Flow.Launcher.Plugin.Program.Programs { await Task.Delay(3000).ConfigureAwait(false); PackageChangeChannel.Reader.TryRead(out _); - await Task.Run(Main.IndexUwpProgramsAsync); + await Main.IndexUwpProgramsAsync(resetCache: true).ConfigureAwait(false); } } } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 274c02404..5c6d155c9 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -789,7 +789,7 @@ namespace Flow.Launcher.Plugin.Program.Programs { } - await Task.Run(Main.IndexWin32ProgramsAsync); + await Main.IndexWin32ProgramsAsync(resetCache: true).ConfigureAwait(false); } } diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-tw.xaml index 3fa06dabc..0d2872d2d 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-tw.xaml @@ -7,14 +7,14 @@ 執行後不關閉命令提示字元視窗 一律以系統管理員身分執行 Use Windows Terminal - Run as different user + 以另一個使用者的身分執行 命令提示字元 Allows to execute system commands from Flow Launcher 此指令已執行了 {0} 次 執行指令 以系統管理員身分執行 複製命令 - Only show number of most used commands: + 僅顯示最常用指令的數量: Command not found: {0} Error running the command: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj index 4cf09baab..1ef6dc495 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj +++ b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj @@ -60,7 +60,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ar.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ar.xaml index b28bd4d9b..10f36502d 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ar.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ar.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + اسم البرنامج وصف diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/cs.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/cs.xaml index a88f113e3..cf16ff8ed 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/cs.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/cs.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + Jméno Popis diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/da.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/da.xaml index 944d5c679..8ac0d48c1 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/da.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/da.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + Name Description diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/de.xaml index 8ef862199..1987257a2 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/de.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/de.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + Name Beschreibung diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml index 9e9a2f93d..33b936ead 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml @@ -3,6 +3,9 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:system="clr-namespace:System;assembly=mscorlib"> + + Skip confirmation when Shutting down, Restarting, or Logging off + Name Description diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/es-419.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/es-419.xaml index 7587fab43..004718b49 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/es-419.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/es-419.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + Name Description diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/es.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/es.xaml index 55d3f457a..0a97e1c17 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/es.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/es.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + Nombre Descripción diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/fr.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/fr.xaml index 6b46ca9dd..48c8fc405 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/fr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/fr.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + Nom Description diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/he.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/he.xaml index 6bd87b60c..5e8df297a 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/he.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/he.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + שם תיאור diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/it.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/it.xaml index 5212a8ce6..8c16cfab5 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/it.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/it.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + Nome Descrizione diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ja.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ja.xaml index a62c09300..0d60abd65 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ja.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ja.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + 名前 説明 diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ko.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ko.xaml index b0c74748d..9df9868c3 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ko.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ko.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + Name 설명 diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/nb.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/nb.xaml index 9c5ea839e..bf4fd46ba 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/nb.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/nb.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + Navn Beskrivelse diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml index 9e1c18a30..1f5b9421f 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + Name Beschrijving diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/pl.xaml index 5aa8e054c..271647107 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/pl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/pl.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + Nazwa Opis diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-br.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-br.xaml index 95639b82f..6c5b08249 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-br.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-br.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + Nome Descrição diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-pt.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-pt.xaml index a3a1411dc..433011195 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-pt.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-pt.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + Nome Descrição diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ru.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ru.xaml index cd5cc111b..3d50ac4e0 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ru.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ru.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + Name Description diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/sk.xaml index 0a9ce9bbd..e5aafbf10 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/sk.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/sk.xaml @@ -1,6 +1,9 @@  + + Preskočiť potvrdenie pri vypínaní, reštartovaní alebo odhlasovaní + Názov Popis diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/sr-Cyrl-RS.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/sr-Cyrl-RS.xaml index 58bb224cc..4a8a704d7 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/sr-Cyrl-RS.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/sr-Cyrl-RS.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + Name Description diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/sr.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/sr.xaml index 4ca394f25..5f570b7fe 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/sr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/sr.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + Name Description diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/tr.xaml index 49fee5a9f..79676e2fb 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/tr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/tr.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + Ad Açıklama diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/uk-UA.xaml index 8f19cdcb3..ff65ac8ac 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/uk-UA.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + Назва Опис diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/vi.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/vi.xaml index ecbcfb66c..58c4ed7e3 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/vi.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/vi.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + Tên Mô Tả diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-cn.xaml index 170bbbd39..c485cce4a 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-cn.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + 名称 描述 diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-tw.xaml index bd705490c..de4af6f2d 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-tw.xaml @@ -1,6 +1,9 @@  + + Skip confirmation when Shutting down, Restarting, or Logging off + 名稱 描述 @@ -33,7 +36,7 @@ 電腦關機 電腦重新啟動 - Restart the computer with Advanced Boot Options for Safe and Debugging modes, as well as other options + 使用進階啟動選項重新啟動裝置,進入安全模式、偵錯模式以及其他模式 登出 鎖定電腦 退出Flow Launcher @@ -43,23 +46,23 @@ 清空資源回收桶 Open recycle bin 索引選項 - Hibernate computer + 休眠 儲存所有Flow Lanuncher設定 - Refreshes plugin data with new content - Open Flow Launcher's log location - Check for new Flow Launcher update - Visit Flow Launcher's documentation for more help and how to use tips - Open the location where Flow Launcher's settings are stored + 使用新內容更新插件資料 + 開啟Flow Launcher的日誌位置 + 檢查Flow Launcher的更新 + 請檢閱Flow Launcher的說明文件以取得更多協助和使用技巧 + 開啟Flow Launcher設定檔的路徑 Toggle Game Mode Quickly change the Flow Launcher theme - All Flow Launcher settings saved - Reloaded all applicable plugin data - Are you sure you want to shut the computer down? - Are you sure you want to restart the computer? - Are you sure you want to restart the computer with Advanced Boot Options? + Flow Launcher的所有設定已儲存 + 已重新載入所有適用的插件資料 + 你是否確定要關機? + 你是否確定要重啟? + 你是否確定要將裝置重啟至進階啟動選項? Are you sure you want to log off? Error Failed to empty the recycle bin. This might happen if:{0}- Some items are currently in use{0}- Some items can't be deleted due to permissions{0}Please close any applications that might be using these files and try again. diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs index a1239d351..db9494e16 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs @@ -175,7 +175,7 @@ namespace Flow.Launcher.Plugin.Sys Privileges = new() { e0 = new LUID_AND_ATTRIBUTES { Luid = luid, Attributes = TOKEN_PRIVILEGES_ATTRIBUTES.SE_PRIVILEGE_ENABLED } } }; - if (!PInvoke.AdjustTokenPrivileges(tokenHandle, false, &privileges, 0, null, null)) + if (!PInvoke.AdjustTokenPrivileges(tokenHandle, false, &privileges, null, out var _)) { return false; } @@ -193,7 +193,7 @@ namespace Flow.Launcher.Plugin.Sys } } - private static List Commands(Query query) + private List Commands(Query query) { var results = new List(); var recycleBinFolder = "shell:RecycleBinFolder"; @@ -206,15 +206,16 @@ namespace Flow.Launcher.Plugin.Sys IcoPath = "Images\\shutdown.png", Action = c => { - var result = Context.API.ShowMsgBox( - Localize.flowlauncher_plugin_sys_dlgtext_shutdown_computer(), - Localize.flowlauncher_plugin_sys_shutdown_computer(), - MessageBoxButton.YesNo, MessageBoxImage.Warning); + var result = _settings.SkipPowerActionConfirmation + ? MessageBoxResult.Yes + : Context.API.ShowMsgBox( + Localize.flowlauncher_plugin_sys_dlgtext_shutdown_computer(), + Localize.flowlauncher_plugin_sys_shutdown_computer(), + MessageBoxButton.YesNo, MessageBoxImage.Warning); + if (result == MessageBoxResult.Yes) { - // Save settings before shutdown to avoid data loss Context.API.SaveAppAllSettings(); - if (EnableShutdownPrivilege()) PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_SHUTDOWN | EXIT_WINDOWS_FLAGS.EWX_POWEROFF, REASON); else @@ -231,15 +232,16 @@ namespace Flow.Launcher.Plugin.Sys IcoPath = "Images\\restart.png", Action = c => { - var result = Context.API.ShowMsgBox( - Localize.flowlauncher_plugin_sys_dlgtext_restart_computer(), - Localize.flowlauncher_plugin_sys_restart_computer(), - MessageBoxButton.YesNo, MessageBoxImage.Warning); + var result = _settings.SkipPowerActionConfirmation + ? MessageBoxResult.Yes + : Context.API.ShowMsgBox( + Localize.flowlauncher_plugin_sys_dlgtext_restart_computer(), + Localize.flowlauncher_plugin_sys_restart_computer(), + MessageBoxButton.YesNo, MessageBoxImage.Warning); + if (result == MessageBoxResult.Yes) { - // Save settings before restart to avoid data loss Context.API.SaveAppAllSettings(); - if (EnableShutdownPrivilege()) PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT, REASON); else @@ -256,10 +258,13 @@ namespace Flow.Launcher.Plugin.Sys IcoPath = "Images\\restart_advanced.png", Action = c => { - var result = Context.API.ShowMsgBox( - Localize.flowlauncher_plugin_sys_dlgtext_restart_computer_advanced(), - Localize.flowlauncher_plugin_sys_restart_computer(), - MessageBoxButton.YesNo, MessageBoxImage.Warning); + var result = _settings.SkipPowerActionConfirmation + ? MessageBoxResult.Yes + : Context.API.ShowMsgBox( + Localize.flowlauncher_plugin_sys_dlgtext_restart_computer_advanced(), + Localize.flowlauncher_plugin_sys_restart_computer(), + MessageBoxButton.YesNo, MessageBoxImage.Warning); + if (result == MessageBoxResult.Yes) { // Save settings before advanced restart to avoid data loss @@ -281,10 +286,13 @@ namespace Flow.Launcher.Plugin.Sys IcoPath = "Images\\logoff.png", Action = c => { - var result = Context.API.ShowMsgBox( - Localize.flowlauncher_plugin_sys_dlgtext_logoff_computer(), - Localize.flowlauncher_plugin_sys_log_off(), - MessageBoxButton.YesNo, MessageBoxImage.Warning); + var result = _settings.SkipPowerActionConfirmation + ? MessageBoxResult.Yes + : Context.API.ShowMsgBox( + Localize.flowlauncher_plugin_sys_dlgtext_logoff_computer(), + Localize.flowlauncher_plugin_sys_log_off(), + MessageBoxButton.YesNo, MessageBoxImage.Warning); + if (result == MessageBoxResult.Yes) PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_LOGOFF, REASON); return true; diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Settings.cs b/Plugins/Flow.Launcher.Plugin.Sys/Settings.cs index 96a545e74..c24c51961 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Sys/Settings.cs @@ -124,4 +124,18 @@ public class Settings : BaseModel [JsonIgnore] public Command SelectedCommand { get; set; } + private bool _skipPowerActionConfirmation; + public bool SkipPowerActionConfirmation + { + get => _skipPowerActionConfirmation; + set + { + if (_skipPowerActionConfirmation == value) + { + return; + } + _skipPowerActionConfirmation = value; + OnPropertyChanged(); + } + } } diff --git a/Plugins/Flow.Launcher.Plugin.Sys/SysSettings.xaml b/Plugins/Flow.Launcher.Plugin.Sys/SysSettings.xaml index 7908bd1e3..fda3128f5 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/SysSettings.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/SysSettings.xaml @@ -12,13 +12,20 @@ + - + + + diff --git a/Plugins/Flow.Launcher.Plugin.Url/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.Url/Languages/zh-tw.xaml index af0a3b175..224d5fe1f 100644 --- a/Plugins/Flow.Launcher.Plugin.Url/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.Url/Languages/zh-tw.xaml @@ -10,13 +10,13 @@ 選擇 應用程式(*.exe)|*.exe|檔案|*.* - Use custom instead of Flow's default web browser - Browser path + 使用自訂瀏覽器而非Flow的預設瀏覽器 + 瀏覽器路徑 新增分頁 新增視窗 - Private mode + 隱私模式 - Prefer https over http + 偏好https而非http diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj index 42176376b..3c9d849c6 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj @@ -50,6 +50,10 @@ + + + + diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ar.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ar.xaml index dda561f00..730f688f9 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ar.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ar.xaml @@ -19,6 +19,7 @@ الرابط بحث Use Search Query Autocomplete + Max Suggestions بيانات الإكمال التلقائي من: يرجى اختيار بحث على الويب هل أنت متأكد أنك تريد حذف {0}؟ diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/cs.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/cs.xaml index 960e63c81..ae5dfd1cc 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/cs.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/cs.xaml @@ -19,6 +19,7 @@ URL Hledat Use Search Query Autocomplete + Max Suggestions Automatické doplnění údajů z: Vyberte webové vyhledávání Opravdu chcete odstranit {0}? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/da.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/da.xaml index 90f20bcc4..693f8443b 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/da.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/da.xaml @@ -19,6 +19,7 @@ URL Search Use Search Query Autocomplete + Max Suggestions Autocomplete Data from: Please select a web search Are you sure you want to delete {0}? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/de.xaml index ed9f35454..7d66efbc4 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/de.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/de.xaml @@ -19,6 +19,7 @@ URL Suche Autovervollständigung von Suchanfragen verwenden + Max Suggestions Autovervollständigung der Daten aus: Bitte wählen Sie eine Websuche aus Sind Sie sicher, dass Sie {0} löschen wollen? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/en.xaml index 5d65e4462..2b2b06fae 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/en.xaml @@ -21,6 +21,7 @@ URL Search Use Search Query Autocomplete + Max Suggestions Autocomplete Data from: Please select a web search Are you sure you want to delete {0}? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/es-419.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/es-419.xaml index 232ead8d5..3be59e2d1 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/es-419.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/es-419.xaml @@ -19,6 +19,7 @@ URL Buscar Use Search Query Autocomplete + Max Suggestions Autocompletar datos de: Por favor, seleccione una búsqueda ¿Seguro que desea eliminar {0}? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/es.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/es.xaml index 41a6325b3..c2c5e50c2 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/es.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/es.xaml @@ -19,6 +19,7 @@ URL Busca en Usar autocompletado en consultas de búsqueda + Max Suggestions Autocompletar datos desde: Por favor, seleccione una búsqueda web ¿Está seguro de que desea eliminar {0}? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/fr.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/fr.xaml index f185a4b50..4318827c8 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/fr.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/fr.xaml @@ -19,6 +19,7 @@ URL Rechercher sur Utiliser la fonction d'auto-complétion des requêtes de recherche + Suggestions max. Saisir automatiquement les données à partir de : Veuillez sélectionner une recherche web Êtes-vous sûr de vouloir supprimer {0} ? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/he.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/he.xaml index e19ba1438..67fa53b5d 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/he.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/he.xaml @@ -19,6 +19,7 @@ כתובת URL חיפו השתמש בהשלמה אוטומטית לשאילתת חיפוש + Max Suggestions השלמה אוטומטית מתוך: בחר שירות חיפוש אינטרנטי האם אתה בטוח שברצונך למחוק את {0}? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/it.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/it.xaml index f6a44354e..a91a650f7 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/it.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/it.xaml @@ -19,6 +19,7 @@ URL Cerca Use Search Query Autocomplete + Max Suggestions Autocompleta i dati da: Seleziona una ricerca web Sei sicuro di voler eliminare {0}? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ja.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ja.xaml index 51aa07bab..89b465154 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ja.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ja.xaml @@ -19,6 +19,7 @@ URL 検索 検索クエリのサジェストを有効にする + Max Suggestions サジェストデータの情報源: web検索を選択してください {0} を削除してもよろしいですか? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ko.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ko.xaml index df46aee90..b1871f922 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ko.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ko.xaml @@ -19,6 +19,7 @@ URL 검색 Use Search Query Autocomplete + Max Suggestions 자동완성 데이터 출처: 웹 검색을 선택하세요 Are you sure you want to delete {0}? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/nb.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/nb.xaml index d648efcdf..758c2bfea 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/nb.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/nb.xaml @@ -19,6 +19,7 @@ Nettadresse Søk Use Search Query Autocomplete + Max Suggestions Autofullfør data fra: Vennligst velg et websøk Er du sikker på at du ønsker å slette {0}? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/nl.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/nl.xaml index 820004fd7..e4b60d329 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/nl.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/nl.xaml @@ -19,6 +19,7 @@ URL Search Use Search Query Autocomplete + Max aantal suggesties Autocomplete Data from: Please select a web search Are you sure you want to delete {0}? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pl.xaml index 1b562a8c2..77573f984 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pl.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pl.xaml @@ -19,6 +19,7 @@ Adres URL Szukaj Użyj autouzupełniania zapytań wyszukiwania + Max Suggestions Autouzupełnianie danych z: Musisz wybrać coś z listy Czy jesteś pewien że chcesz usunąć {0}? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pt-br.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pt-br.xaml index 6a6704b95..a701a8ec6 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pt-br.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pt-br.xaml @@ -19,6 +19,7 @@ URL Search Use Search Query Autocomplete + Max Suggestions Autocomplete Data from: Please select a web search Are you sure you want to delete {0}? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pt-pt.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pt-pt.xaml index 21c0ff7cd..808043a2b 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pt-pt.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/pt-pt.xaml @@ -19,6 +19,7 @@ URL Pesquisar Utilizar conclusão automática para as consultas + Máximo de sugestões Preencher dados a partir de: Selecione uma pesquisa web Tem a certeza de que deseja eliminar {0}? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ru.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ru.xaml index 4435ba9c9..9a88a2d0c 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ru.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/ru.xaml @@ -19,6 +19,7 @@ URL Поиск Use Search Query Autocomplete + Max Suggestions Autocomplete Data from: Please select a web search Вы уверены, что хотите удалить {0}? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/sk.xaml index 698283c23..a1becbabf 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/sk.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/sk.xaml @@ -19,6 +19,7 @@ Adresa URL Hľadať Použiť automatické dokončovanie výrazov vyhľadávania + Maximum návrhov Automatické dokončovanie údajov z: Vyberte webové vyhľadávanie Naozaj chcete odstrániť {0}? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/sr-Cyrl-RS.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/sr-Cyrl-RS.xaml index 98a65928b..9a8baaa09 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/sr-Cyrl-RS.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/sr-Cyrl-RS.xaml @@ -19,6 +19,7 @@ URL Search Use Search Query Autocomplete + Max Suggestions Autocomplete Data from: Please select a web search Are you sure you want to delete {0}? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/sr.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/sr.xaml index 205d8edcc..09f971117 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/sr.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/sr.xaml @@ -19,6 +19,7 @@ URL Search Use Search Query Autocomplete + Max Suggestions Autocomplete Data from: Please select a web search Are you sure you want to delete {0}? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/tr.xaml index e706b7bf7..6d36fff80 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/tr.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/tr.xaml @@ -19,6 +19,7 @@ URL Ara: Arama Sorgusu Otomatik Doldurmayı Kullan + Maksimum Öneriler Otomatik Doldurma Verileri: Lütfen bir web araması seçin {0} bağlantısını silmek istediğinize emin misiniz? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/uk-UA.xaml index 2163c29be..a9121ee99 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/uk-UA.xaml @@ -19,6 +19,7 @@ URL Пошук Використовувати автозаповнення пошукового запиту + Максимум пропозицій Автозаповнення даних з: Будь ласка, виберіть пошуковий запит в Інтернеті Ви впевнені, що хочете видалити {0}? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/vi.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/vi.xaml index 1da790eee..d86a98b12 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/vi.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/vi.xaml @@ -19,6 +19,7 @@ Địa chỉ URL Tìm kiếm Use Search Query Autocomplete + Max Suggestions Tự động hoàn thành dữ liệu từ: Vui lòng chọn tìm kiếm trên web Bạn có chắc chắn muốn xóa {0} không? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/zh-cn.xaml index 230c587c3..7178358e1 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/zh-cn.xaml @@ -19,6 +19,7 @@ 打开链接 搜索 使用搜索查询自动补全 + 最大建议数 自动补全数据: 请选择一项 您确定要删除 {0} 吗? diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/zh-tw.xaml index 9bb37f2ec..22fcab5bb 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/zh-tw.xaml @@ -2,7 +2,7 @@ 搜尋來源設定 - Open search in: + 在以下位置開啟搜尋: 新增視窗 新索引標籤 設定瀏覽器路徑: @@ -19,6 +19,7 @@ 網址 搜尋 Use Search Query Autocomplete + Max Suggestions 從以下位置自動填入資料: 請選擇一項 你確認要刪除{0}嗎 @@ -46,7 +47,7 @@ 觸發關鍵字已經存在,請選擇一個新的關鍵字 操作成功 Failed to update search source. The item may have been removed. - Hint: You do not need to place custom images in this directory, if Flow's version is updated they will be lost. Flow will automatically copy any images outside of this directory across to WebSearch's custom image location. + 提示:您無需將自訂圖片放置在此目錄中,如果 Flow 版本更新,這些圖片將會遺失。 Flow 會自動將此目錄以外的所有圖片複製到 WebSearch 的自訂圖片位置。 網頁搜尋 提供網頁搜尋功能 diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs index b9b8a0b19..299e27499 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -91,6 +91,7 @@ namespace Flow.Launcher.Plugin.WebSearch if (token.IsCancellationRequested) return null; + } return results; @@ -126,7 +127,7 @@ namespace Flow.Launcher.Plugin.WebSearch token.ThrowIfCancellationRequested(); - var resultsFromSuggestion = suggestions?.Select(o => new Result + var resultsFromSuggestion = suggestions?.Take(_settings.MaxSuggestions).Select(o => new Result { Title = o, SubTitle = subtitle, diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs index 0c0ac4b84..31540ad92 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs @@ -205,6 +205,23 @@ namespace Flow.Launcher.Plugin.WebSearch } } + private int maxSuggestions = 1; + public int MaxSuggestions + { + get => maxSuggestions; + set + { + if (value > 0 && value <= 1000) + { + if (maxSuggestions != value) + { + maxSuggestions = value; + OnPropertyChanged(); + } + } + } + } + [JsonIgnore] public SuggestionSource[] Suggestions { get; set; } = { new Google(), diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml index 152558e81..2c0841253 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml @@ -3,7 +3,9 @@ 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:ikw="http://schemas.inkore.net/lib/ui/wpf" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" xmlns:vm="clr-namespace:Flow.Launcher.Plugin.WebSearch" d:DataContext="{d:DesignInstance vm:SettingsViewModel}" d:DesignHeight="300" @@ -45,17 +47,17 @@ x:Name="SearchSourcesListView" Grid.Row="0" Margin="{StaticResource SettingPanelItemTopBottomMargin}" + AllowDrop="True" BorderBrush="DarkGray" BorderThickness="1" + Drop="ListView_Drop" GridViewColumnHeader.Click="SortByColumn" ItemsSource="{Binding Settings.SearchSources}" MouseDoubleClick="MouseDoubleClickItem" - SelectedItem="{Binding Settings.SelectedSearchSource}" - SizeChanged="ListView_SizeChanged" PreviewMouseLeftButtonDown="ListView_PreviewMouseLeftButtonDown" PreviewMouseMove="ListView_PreviewMouseMove" - AllowDrop="True" - Drop="ListView_Drop" + SelectedItem="{Binding Settings.SelectedSearchSource}" + SizeChanged="ListView_SizeChanged" Style="{StaticResource {x:Static GridView.GridViewStyleKey}}"> @@ -146,31 +148,43 @@ - - - + \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml.cs index 6dc766fff..1c1dd3116 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml.cs @@ -240,5 +240,15 @@ namespace Flow.Launcher.Plugin.WebSearch } return null; } + + // This is used for NumberBox to force its value to be 1 when the user clears the value + private void NumberBox_ValueChanged(iNKORE.UI.WPF.Modern.Controls.NumberBox sender, iNKORE.UI.WPF.Modern.Controls.NumberBoxValueChangedEventArgs args) + { + if (double.IsNaN(args.NewValue)) + { + sender.Value = 1; + _settings.MaxSuggestions = (int)sender.Value; + } + } } } diff --git a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.zh-TW.resx b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.zh-TW.resx index ac04a16bf..52223652f 100644 --- a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.zh-TW.resx +++ b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.zh-TW.resx @@ -1639,7 +1639,7 @@ 視窗框線 - Windows Anytime Upgrade + Windows 隨時升級 Area Control Panel (legacy settings) diff --git a/README.md b/README.md index f2f7a495d..2dbeb8b42 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,9 @@ 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.

+Dedicated to making your workflow 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 :)

+

Remember to star it, Flow will love you more :)

@@ -79,7 +79,7 @@ Or download the [early access version](https://github.com/Flow-Launcher/Prerelea -- Support search using environment variable paths. +- Supports search using environment variable paths. ### Web Searches & URLs @@ -135,13 +135,13 @@ Or download the [early access version](https://github.com/Flow-Launcher/Prerelea - Drag a file/folder to File Explorer, or even Discord. -- Copy/move behavior can be change via Ctrl or Shift, and the operation is displayed on the mouse cursor. +- Copy/move behavior can be changed via Ctrl or Shift, and the operation is displayed on the mouse cursor. ### Priority -- Prioritise the order of each plugin's results. +- Prioritize the order of each plugin's results. ### Preview Panel @@ -228,7 +228,7 @@ Or download the [early access version](https://github.com/Flow-Launcher/Prerelea ## 📦 Plugins - Support wide range of plugins. Visit [here](https://www.flowlauncher.com/plugins/) for our plugin portfolio. -- Publish your own plugin to flow! Create plugins in: +- Publish your own plugin to Flow! Create plugins in:

@@ -246,9 +246,9 @@ Or download the [early access version](https://github.com/Flow-Launcher/Prerelea -### [Clipboard History](https://github.com/liberize/Flow.Launcher.Plugin.ClipboardHistory) +### [Clipboard+](https://github.com/Jack251970/Flow.Launcher.Plugin.ClipboardPlus) - + ### [Home Assistant Commander](https://github.com/Garulf/HA-Commander) @@ -327,7 +327,7 @@ Or download the [early access version](https://github.com/Flow-Launcher/Prerelea | Check For Update | Check for new Flow Launcher update | | Open Log Location | Open Flow Launcher's log location | | Index Option | Open Windows Search Index window | -| Flow Launcher Tips | Visit Flow Launcher's documentation for more help and how to use tips | +| Flow Launcher Tips | Visit Flow Launcher's documentation for more help and usage tips | | Flow Launcher UserData Folder | Open the location where Flow Launcher's settings are stored | | Toggle Game Mode | Toggle Game Mode | | Set Flow Launcher Theme | Set the Flow Launcher Theme | @@ -348,8 +348,8 @@ Or download the [early access version](https://github.com/Flow-Launcher/Prerelea - - + +

@@ -397,9 +397,9 @@ Our UI library is using [iNKORE.UI.WPF.Modern](https://github.com/iNKORE-NET/UI. iNKORE.UI.WPF.Modern -### New changes +### New Changes -All changes to flow are captured via pull requests. Some new changes will have been merged but still pending release, this means while a change may not exist in the current release, it may very well have been accepted and merged into the dev branch and available as a pre-release download. It is therefore a good idea to search through the open and closed pull requests before you start to make changes to ensure the change you intend to make is not already done. +All changes to Flow are captured via pull requests. Some new changes may have been merged but are still pending release. This means that while a change may not exist in the current release, it may have been accepted and merged into the dev branch and is available as a pre-release download. It is therefore a good idea to search through the open and closed pull requests before you start to make changes to ensure the change you intend to make is not already done. Each of the pull requests will be marked with a milestone indicating the planned release version for the change. @@ -407,7 +407,7 @@ Each of the pull requests will be marked with a milestone indicating the planned Contributions are very welcome, in addition to the main project (C#) there are also [documentation](https://github.com/Flow-Launcher/docs) (md), [website](https://github.com/Flow-Launcher/flow-launcher.github.io) (html/css) and [others](https://github.com/Flow-Launcher) that can be contributed to. If you are unsure of a change you want to make, let us know in the [Discussions](https://github.com/Flow-Launcher/Flow.Launcher/discussions/categories/ideas), otherwise feel free to submit a pull request. -You will find the main goals of flow placed under the [Projects board](https://github.com/orgs/Flow-Launcher/projects/4), so feel free to contribute on that. If you would like to make small incremental changes, feel free to do so as well. +You will find the main goals of Flow placed under the [Projects board](https://github.com/orgs/Flow-Launcher/projects/4), so feel free to contribute on that. If you would like to make small incremental changes, feel free to do so as well. Get in touch if you would like to join the Flow-Launcher Team and help build this great tool. diff --git a/appveyor.yml b/appveyor.yml index 03542e8d2..467ddefbe 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: '2.0.3.{build}' +version: '2.1.0.{build}' # Do not build on tags because we create a release on merge to master. Otherwise will upload artifacts twice changing the hash, as well as triggering duplicate GitHub release action & NuGet deployments. skip_tags: true