diff --git a/.github/workflows/default_plugins.yml b/.github/workflows/default_plugins.yml index 59cedc1e8..d0ba62c17 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@v4 + - uses: actions/checkout@v5 - name: Setup .NET uses: actions/setup-dotnet@v4 with: diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 988548bee..9a09198fe 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@v4 + - uses: actions/checkout@v5 - name: Set Flow.Launcher.csproj version id: update uses: vers-one/dotnet-project-version-updater@v1.7 diff --git a/.github/workflows/release_pr.yml b/.github/workflows/release_pr.yml index 451bf386c..65946755b 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@v4 + - uses: actions/checkout@v5 - uses: actions/setup-python@v5 with: diff --git a/Directory.Build.props b/Directory.Build.props index fa499273c..a5545af12 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,5 +1,7 @@ true + + false \ No newline at end of file diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs index 455ee096d..89286dfb0 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs @@ -11,6 +11,8 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments { internal class PythonEnvironment : AbstractPluginEnvironment { + private static readonly string ClassName = nameof(PythonEnvironment); + internal override string Language => AllowedLanguage.Python; internal override string EnvName => DataLocation.PythonEnvironmentName; @@ -39,9 +41,20 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments // Python 3.11.4 is no longer Windows 7 compatible. If user is on Win 7 and // uses Python plugin they need to custom install and use v3.8.9 - JTF.Run(() => DroplexPackage.Drop(App.python_3_11_4_embeddable, InstallPath)); + JTF.Run(async () => + { + try + { + await DroplexPackage.Drop(App.python_3_11_4_embeddable, InstallPath); - PluginsSettingsFilePath = ExecutablePath; + PluginsSettingsFilePath = ExecutablePath; + } + catch (System.Exception e) + { + API.ShowMsgError(API.GetTranslation("failToInstallPythonEnv")); + API.LogException(ClassName, "Failed to install Python environment", e); + } + }); } internal override PluginPair CreatePluginPair(string filePath, PluginMetadata metadata) diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs index 12965286f..724ae20f4 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs @@ -11,6 +11,8 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments { internal class TypeScriptEnvironment : AbstractPluginEnvironment { + private static readonly string ClassName = nameof(TypeScriptEnvironment); + internal override string Language => AllowedLanguage.TypeScript; internal override string EnvName => DataLocation.NodeEnvironmentName; @@ -34,9 +36,20 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments { FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s)); - JTF.Run(() => DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath)); + JTF.Run(async () => + { + try + { + await DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath); - PluginsSettingsFilePath = ExecutablePath; + PluginsSettingsFilePath = ExecutablePath; + } + catch (System.Exception e) + { + API.ShowMsgError(API.GetTranslation("failToInstallTypeScriptEnv")); + API.LogException(ClassName, "Failed to install TypeScript environment", e); + } + }); } internal override PluginPair CreatePluginPair(string filePath, PluginMetadata metadata) diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs index 6960b79c9..6a32664a1 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs @@ -11,6 +11,8 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments { internal class TypeScriptV2Environment : AbstractPluginEnvironment { + private static readonly string ClassName = nameof(TypeScriptV2Environment); + internal override string Language => AllowedLanguage.TypeScriptV2; internal override string EnvName => DataLocation.NodeEnvironmentName; @@ -34,9 +36,20 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments { FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s)); - JTF.Run(() => DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath)); + JTF.Run(async () => + { + try + { + await DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath); - PluginsSettingsFilePath = ExecutablePath; + PluginsSettingsFilePath = ExecutablePath; + } + catch (System.Exception e) + { + API.ShowMsgError(API.GetTranslation("failToInstallTypeScriptEnv")); + API.LogException(ClassName, "Failed to install TypeScript environment", e); + } + }); } internal override PluginPair CreatePluginPair(string filePath, PluginMetadata metadata) diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 9d511297e..e9e5ee367 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -124,13 +124,9 @@ namespace Flow.Launcher.Core.Plugin API.GetTranslation("pluginsHaveErrored") : API.GetTranslation("pluginHasErrored"); - _ = Task.Run(() => - { - API.ShowMsgBox($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + - $"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" + - API.GetTranslation("referToLogs"), string.Empty, - MessageBoxButton.OK, MessageBoxImage.Warning); - }); + API.ShowMsgError($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + + $"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" + + API.GetTranslation("referToLogs")); } return plugins; diff --git a/Flow.Launcher.Core/Resource/AvailableLanguages.cs b/Flow.Launcher.Core/Resource/AvailableLanguages.cs index ecaecf646..5534ea172 100644 --- a/Flow.Launcher.Core/Resource/AvailableLanguages.cs +++ b/Flow.Launcher.Core/Resource/AvailableLanguages.cs @@ -17,6 +17,7 @@ namespace Flow.Launcher.Core.Resource public static Language German = new Language("de", "Deutsch"); public static Language Korean = new Language("ko", "한국어"); public static Language Serbian = new Language("sr", "Srpski"); + public static Language Serbian_Cyrillic = new Language("sr-Cyrl-RS", "Српски"); public static Language Portuguese_Portugal = new Language("pt-pt", "Português"); public static Language Portuguese_Brazil = new Language("pt-br", "Português (Brasil)"); public static Language Spanish = new Language("es", "Spanish"); @@ -47,6 +48,7 @@ namespace Flow.Launcher.Core.Resource German, Korean, Serbian, + Serbian_Cyrillic, Portuguese_Portugal, Portuguese_Brazil, Spanish, @@ -79,7 +81,8 @@ namespace Flow.Launcher.Core.Resource "da" => "System", "de" => "System", "ko" => "시스템", - "sr" => "Систем", + "sr" => "Sistem", + "sr-Cyrl-RS" => "Систем", "pt-pt" => "Sistema", "pt-br" => "Sistema", "es" => "Sistema", diff --git a/Flow.Launcher.Core/packages.lock.json b/Flow.Launcher.Core/packages.lock.json index caec08ebf..5e9abc24c 100644 --- a/Flow.Launcher.Core/packages.lock.json +++ b/Flow.Launcher.Core/packages.lock.json @@ -161,8 +161,8 @@ }, "Microsoft.Win32.SystemEvents": { "type": "Transitive", - "resolved": "9.0.7", - "contentHash": "lFGY2aGgmMREPJEfOmZcA6v0CLjWVpcfNHRgqYMoSQhy80+GxhYqdW5xe+DCLrVqE1M7/0RpOkIo49/KH/cd/A==" + "resolved": "7.0.0", + "contentHash": "2nXPrhdAyAzir0gLl8Yy8S5Mnm/uBSQQA7jEsILOS1MTyS7DbmV1NgViMtvV1sfCD1ebITpNwb1NIinKeJgUVQ==" }, "Mono.Cecil": { "type": "Transitive", @@ -222,10 +222,10 @@ }, "System.Drawing.Common": { "type": "Transitive", - "resolved": "9.0.7", - "contentHash": "1k/Pk7hcM3vP2tfIRRS2ECCCN7ya+hvocsM1JMc4ZDCU6qw7yOuUmqmCDfgXZ4Q4FS6jass2EAai5ByKodDi0g==", + "resolved": "7.0.0", + "contentHash": "KIX+oBU38pxkKPxvLcLfIkOV5Ien8ReN78wro7OF5/erwcmortzeFx+iBswlh2Vz6gVne0khocQudGwaO1Ey6A==", "dependencies": { - "Microsoft.Win32.SystemEvents": "9.0.7" + "Microsoft.Win32.SystemEvents": "7.0.0" } }, "System.IO.Pipelines": { @@ -262,7 +262,7 @@ "NLog": "[6.0.1, )", "NLog.OutputDebugString": "[6.0.1, )", "SharpVectors.Wpf": "[1.8.4.2, )", - "System.Drawing.Common": "[9.0.7, )", + "System.Drawing.Common": "[7.0.0, )", "ToolGood.Words.Pinyin": "[3.1.0.3, )" } }, diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index 51b1d5175..c32c36248 100644 --- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj +++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj @@ -74,7 +74,9 @@ all - + + + diff --git a/Flow.Launcher.Infrastructure/packages.lock.json b/Flow.Launcher.Infrastructure/packages.lock.json index 87b4bb6da..abd250f7c 100644 --- a/Flow.Launcher.Infrastructure/packages.lock.json +++ b/Flow.Launcher.Infrastructure/packages.lock.json @@ -108,11 +108,11 @@ }, "System.Drawing.Common": { "type": "Direct", - "requested": "[9.0.7, )", - "resolved": "9.0.7", - "contentHash": "1k/Pk7hcM3vP2tfIRRS2ECCCN7ya+hvocsM1JMc4ZDCU6qw7yOuUmqmCDfgXZ4Q4FS6jass2EAai5ByKodDi0g==", + "requested": "[7.0.0, )", + "resolved": "7.0.0", + "contentHash": "KIX+oBU38pxkKPxvLcLfIkOV5Ien8ReN78wro7OF5/erwcmortzeFx+iBswlh2Vz6gVne0khocQudGwaO1Ey6A==", "dependencies": { - "Microsoft.Win32.SystemEvents": "9.0.7" + "Microsoft.Win32.SystemEvents": "7.0.0" } }, "ToolGood.Words.Pinyin": { @@ -156,8 +156,8 @@ }, "Microsoft.Win32.SystemEvents": { "type": "Transitive", - "resolved": "9.0.7", - "contentHash": "lFGY2aGgmMREPJEfOmZcA6v0CLjWVpcfNHRgqYMoSQhy80+GxhYqdW5xe+DCLrVqE1M7/0RpOkIo49/KH/cd/A==" + "resolved": "7.0.0", + "contentHash": "2nXPrhdAyAzir0gLl8Yy8S5Mnm/uBSQQA7jEsILOS1MTyS7DbmV1NgViMtvV1sfCD1ebITpNwb1NIinKeJgUVQ==" }, "Microsoft.Windows.SDK.Win32Docs": { "type": "Transitive", diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index 07cff6fd0..56b344789 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -57,7 +57,10 @@ namespace Flow.Launcher.Plugin /// for user on the plugin result. If autocomplete action for example is tab, pressing tab will have /// the default constructed autocomplete text (result's Title), or the text provided here if not empty. /// - /// When a value is not set, the will be used. + /// + /// When a value is not set, the will be used. + /// Please include the action keyword prefix when necessary because Flow does not prepend it automatically. + /// public string AutoCompleteText { get; set; } /// @@ -257,6 +260,17 @@ namespace Flow.Launcher.Plugin /// public bool ShowBadge { get; set; } = false; + /// + /// This holds the text which can be shown as a query suggestion. + /// + /// + /// When a value is not set, the will be used. + /// Do not include the action keyword prefix because Flow prepends it automatically. + /// If the it does not start with the query text, it will not be shown as a suggestion. + /// So make sure to set this value to start with the query text. + /// + public string QuerySuggestionText { get; set; } + /// /// List of hotkey IDs that are supported for this result. /// Those hotkeys should be registered by IPluginHotkey interface. @@ -314,6 +328,7 @@ namespace Flow.Launcher.Plugin AddSelectedCount = AddSelectedCount, RecordKey = RecordKey, ShowBadge = ShowBadge, + QuerySuggestionText = QuerySuggestionText, HotkeyIds = HotkeyIds, }; } diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 6e053db29..0360c761e 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -297,6 +297,7 @@ namespace Flow.Launcher }); } + [Conditional("RELEASE")] private static void AutoPluginUpdates() { _ = Task.Run(async () => diff --git a/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs b/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs index eb492e334..c2d7d016c 100644 --- a/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs +++ b/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs @@ -33,15 +33,39 @@ public class QuerySuggestionBoxConverter : IMultiValueConverter { var selectedResult = selectedItem.Result; var selectedResultActionKeyword = string.IsNullOrEmpty(selectedResult.ActionKeywordAssigned) ? "" : selectedResult.ActionKeywordAssigned + " "; - var selectedResultPossibleSuggestion = selectedResultActionKeyword + selectedResult.Title; - if (!selectedResultPossibleSuggestion.StartsWith(queryText, StringComparison.CurrentCultureIgnoreCase)) + string selectedResultPossibleSuggestion = null; + + // Firstly check if the result has QuerySuggestionText + if (!string.IsNullOrEmpty(selectedResult.QuerySuggestionText)) + { + selectedResultPossibleSuggestion = selectedResultActionKeyword + selectedResult.QuerySuggestionText; + + // If this QuerySuggestionText does not start with the queryText, set it to null + if (!selectedResultPossibleSuggestion.StartsWith(queryText, StringComparison.CurrentCultureIgnoreCase)) + { + selectedResultPossibleSuggestion = null; + } + } + + // Then check Title as suggestion + if (string.IsNullOrEmpty(selectedResultPossibleSuggestion)) + { + selectedResultPossibleSuggestion = selectedResultActionKeyword + selectedResult.Title; + + // If this QuerySuggestionText does not start with the queryText, set it to null + if (!selectedResultPossibleSuggestion.StartsWith(queryText, StringComparison.CurrentCultureIgnoreCase)) + { + selectedResultPossibleSuggestion = null; + } + } + + if (string.IsNullOrEmpty(selectedResultPossibleSuggestion)) return string.Empty; - // For AutocompleteQueryCommand. // When user typed lower case and result title is uppercase, we still want to display suggestion - selectedItem.QuerySuggestionText = queryText + selectedResultPossibleSuggestion.Substring(queryText.Length); + selectedItem.QuerySuggestionText = string.Concat(queryText, selectedResultPossibleSuggestion.AsSpan(queryText.Length)); // Check if Text will be larger than our QueryTextBox Typeface typeface = new Typeface(queryTextBox.FontFamily, queryTextBox.FontStyle, queryTextBox.FontWeight, queryTextBox.FontStretch); diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 7db769c70..fe84b5e1e 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -39,6 +39,48 @@ false + + + + + + + + @@ -136,7 +178,6 @@ - diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 69548c328..fc63ca80a 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -34,6 +34,10 @@ Please try again Unable to parse Http Proxy + + Failed to install TypeScript environment. Please try again later + Failed to install Python environment. Please try again later. + 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 diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 70b6c1bf4..21d3ae9b9 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -371,10 +371,8 @@ MinHeight="380" MaxHeight="{Binding ElementName=ResultListBox, Path=ActualHeight}" Padding="0 0 10 10" - d:DataContext="{d:DesignInstance vm:ResultViewModel}" - DataContext="{Binding PreviewSelectedItem, Mode=OneWay}" Visibility="{Binding ShowCustomizedPreview}"> - + diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index e0ed105cf..38ca18147 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -8,6 +8,7 @@ using System.IO; using System.Linq; using System.Net; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using System.Windows; @@ -379,24 +380,35 @@ namespace Flow.Launcher explorer.Start(); } } + catch (COMException ex) when (ex.ErrorCode == unchecked((int)0x80004004)) + { + /* + * The COMException with HResult 0x80004004 is E_ABORT (operation aborted). + * Shell APIs often return this when the operation is canceled or the shell cannot complete it cleanly. + * It most likely comes from Win32Helper.OpenFolderAndSelectFile(targetPath). + * Typical triggers: + * The target file/folder was deleted/moved between computing targetPath and the shell call. + * The folder is on an offline network/removable drive. + * Explorer is restarting/busy and aborts the request. + * A selection request to a new/closing Explorer window is canceled. + * Because it is commonly user- or environment-driven and not actionable, + * we should treat it as expected noise and ignore it to avoid bothering users. + */ + } catch (Win32Exception ex) when (ex.NativeErrorCode == 2) { LogError(ClassName, "File Manager not found"); - ShowMsgBox( - string.Format(GetTranslation("fileManagerNotFound"), ex.Message), + ShowMsgError( GetTranslation("fileManagerNotFoundTitle"), - MessageBoxButton.OK, - MessageBoxImage.Error + string.Format(GetTranslation("fileManagerNotFound"), ex.Message) ); } catch (Exception ex) { LogException(ClassName, "Failed to open folder", ex); - ShowMsgBox( - string.Format(GetTranslation("folderOpenError"), ex.Message), + ShowMsgError( GetTranslation("errorTitle"), - MessageBoxButton.OK, - MessageBoxImage.Error + string.Format(GetTranslation("folderOpenError"), ex.Message) ); } } @@ -424,11 +436,9 @@ namespace Flow.Launcher { var tabOrWindow = browserInfo.OpenInTab ? "tab" : "window"; LogException(ClassName, $"Failed to open URL in browser {tabOrWindow}: {path}, {inPrivate ?? browserInfo.EnablePrivate}, {browserInfo.PrivateArg}", e); - ShowMsgBox( - GetTranslation("browserOpenError"), + ShowMsgError( GetTranslation("errorTitle"), - MessageBoxButton.OK, - MessageBoxImage.Error + GetTranslation("browserOpenError") ); } } diff --git a/Flow.Launcher/SettingPages/Views/SettingsPanePlugins.xaml b/Flow.Launcher/SettingPages/Views/SettingsPanePlugins.xaml index 52d77f914..aa7c219e5 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPanePlugins.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPanePlugins.xaml @@ -122,6 +122,7 @@ FontSize="14" ItemContainerStyle="{StaticResource PluginList}" ItemsSource="{Binding Source={StaticResource PluginCollectionView}}" + Loaded="PluginListBox_Loaded" ScrollViewer.CanContentScroll="False" ScrollViewer.HorizontalScrollBarVisibility="Disabled" SnapsToDevicePixels="True" diff --git a/Flow.Launcher/SettingPages/Views/SettingsPanePlugins.xaml.cs b/Flow.Launcher/SettingPages/Views/SettingsPanePlugins.xaml.cs index f486a3443..e77a30843 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPanePlugins.xaml.cs +++ b/Flow.Launcher/SettingPages/Views/SettingsPanePlugins.xaml.cs @@ -1,4 +1,6 @@ using System.ComponentModel; +using System.Windows; +using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; using System.Windows.Navigation; @@ -33,6 +35,14 @@ public partial class SettingsPanePlugins base.OnNavigatedTo(e); } + private void PluginListBox_Loaded(object sender, RoutedEventArgs e) + { + // After list is loaded, we need to clear selection to make sure all items can be mouse hovered + // because the selected item cannot be hovered. + if (sender is not ListBox listBox) return; + listBox.SelectedIndex = -1; + } + private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(SettingsPanePluginsViewModel.FilterText)) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index dba0561b2..56c1e112f 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -9,6 +9,7 @@ using System.Threading.Channels; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; +using System.Windows.Controls; using System.Windows.Media; using System.Windows.Threading; using CommunityToolkit.Mvvm.DependencyInjection; @@ -890,6 +891,12 @@ namespace Flow.Launcher.ViewModel } } + public Visibility ShowCustomizedPreview + => InternalPreviewVisible && PreviewSelectedItem?.Result.PreviewPanel != null ? Visibility.Visible : Visibility.Collapsed; + + public UserControl CustomizedPreviewControl + => ShowCustomizedPreview == Visibility.Visible ? PreviewSelectedItem?.Result.PreviewPanel.Value : null; + public Visibility ProgressBarVisibility { get; set; } public Visibility MainWindowVisibility { get; set; } diff --git a/Flow.Launcher/ViewModel/PluginViewModel.cs b/Flow.Launcher/ViewModel/PluginViewModel.cs index ea222d023..d889bdd52 100644 --- a/Flow.Launcher/ViewModel/PluginViewModel.cs +++ b/Flow.Launcher/ViewModel/PluginViewModel.cs @@ -37,12 +37,17 @@ namespace Flow.Launcher.ViewModel OnPropertyChanged(nameof(Image)); } + private bool _imageLoaded = false; + public ImageSource Image { get { - if (_image == ImageLoader.MissingImage) + if (!_imageLoaded) + { + _imageLoaded = true; _ = LoadIconAsync(); + } return _image; } diff --git a/Flow.Launcher/ViewModel/ResultViewModel.cs b/Flow.Launcher/ViewModel/ResultViewModel.cs index c58abae28..d4382fb7f 100644 --- a/Flow.Launcher/ViewModel/ResultViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultViewModel.cs @@ -66,8 +66,6 @@ namespace Flow.Launcher.ViewModel public Visibility ShowDefaultPreview => Result.PreviewPanel == null ? Visibility.Visible : Visibility.Collapsed; - public Visibility ShowCustomizedPreview => Result.PreviewPanel == null ? Visibility.Collapsed : Visibility.Visible; - public Visibility ShowIcon { get diff --git a/Flow.Launcher/packages.lock.json b/Flow.Launcher/packages.lock.json index 1047b1f3f..32b78c334 100644 --- a/Flow.Launcher/packages.lock.json +++ b/Flow.Launcher/packages.lock.json @@ -552,8 +552,8 @@ }, "Microsoft.Win32.SystemEvents": { "type": "Transitive", - "resolved": "9.0.7", - "contentHash": "lFGY2aGgmMREPJEfOmZcA6v0CLjWVpcfNHRgqYMoSQhy80+GxhYqdW5xe+DCLrVqE1M7/0RpOkIo49/KH/cd/A==" + "resolved": "7.0.0", + "contentHash": "2nXPrhdAyAzir0gLl8Yy8S5Mnm/uBSQQA7jEsILOS1MTyS7DbmV1NgViMtvV1sfCD1ebITpNwb1NIinKeJgUVQ==" }, "Mono.Cecil": { "type": "Transitive", @@ -677,10 +677,10 @@ }, "System.Drawing.Common": { "type": "Transitive", - "resolved": "9.0.7", - "contentHash": "1k/Pk7hcM3vP2tfIRRS2ECCCN7ya+hvocsM1JMc4ZDCU6qw7yOuUmqmCDfgXZ4Q4FS6jass2EAai5ByKodDi0g==", + "resolved": "7.0.0", + "contentHash": "KIX+oBU38pxkKPxvLcLfIkOV5Ien8ReN78wro7OF5/erwcmortzeFx+iBswlh2Vz6gVne0khocQudGwaO1Ey6A==", "dependencies": { - "Microsoft.Win32.SystemEvents": "9.0.7" + "Microsoft.Win32.SystemEvents": "7.0.0" } }, "System.Globalization": { @@ -862,7 +862,7 @@ "NLog": "[6.0.1, )", "NLog.OutputDebugString": "[6.0.1, )", "SharpVectors.Wpf": "[1.8.4.2, )", - "System.Drawing.Common": "[9.0.7, )", + "System.Drawing.Common": "[7.0.0, )", "ToolGood.Words.Pinyin": "[3.1.0.3, )" } }, diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index 68e5d5caa..6a099cb07 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -2,6 +2,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; +using System.IO.Compression; using System.Linq; using System.Threading.Tasks; using Flow.Launcher.Plugin.BrowserBookmark.Helper; @@ -134,10 +135,6 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader try { - if (string.IsNullOrEmpty(bookmark.Url)) - return; - - // Extract domain from URL if (!Uri.TryCreate(bookmark.Url, UriKind.Absolute, out Uri uri)) return; @@ -146,43 +143,49 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader // Query for latest Firefox version favicon structure using var cmd = connection.CreateCommand(); cmd.CommandText = @" - SELECT i.data + SELECT i.id, i.data FROM moz_icons i JOIN moz_icons_to_pages ip ON i.id = ip.icon_id JOIN moz_pages_w_icons p ON ip.page_id = p.id - WHERE p.page_url LIKE @url - AND i.data IS NOT NULL - ORDER BY i.width DESC -- Select largest icon available + WHERE p.page_url LIKE @domain + ORDER BY i.width DESC LIMIT 1"; - cmd.Parameters.AddWithValue("@url", $"%{domain}%"); + cmd.Parameters.AddWithValue("@domain", $"%{domain}%"); using var reader = cmd.ExecuteReader(); - if (!reader.Read() || reader.IsDBNull(0)) + if (!reader.Read() || reader.IsDBNull(1)) return; + var iconId = reader.GetInt64(0).ToString(); var imageData = (byte[])reader["data"]; if (imageData is not { Length: > 0 }) return; - string faviconPath; - if (FaviconHelper.IsSvgData(imageData)) + // Check if the image data is compressed (GZip) + if (imageData.Length > 2 && imageData[0] == 0x1f && imageData[1] == 0x8b) { - faviconPath = Path.Combine(_faviconCacheDir, $"firefox_{domain}.svg"); - } - else - { - faviconPath = Path.Combine(_faviconCacheDir, $"firefox_{domain}.png"); + using var inputStream = new MemoryStream(imageData); + using var gZipStream = new GZipStream(inputStream, CompressionMode.Decompress); + using var outputStream = new MemoryStream(); + gZipStream.CopyTo(outputStream); + imageData = outputStream.ToArray(); } - // Filter out duplicate favicons - if (savedPaths.TryAdd(faviconPath, true)) + // Convert the image data to WebP format + var webpData = FaviconHelper.TryConvertToWebp(imageData); + if (webpData != null) { - FaviconHelper.SaveBitmapData(imageData, faviconPath); - } + var faviconPath = Path.Combine(_faviconCacheDir, $"firefox_{domain}_{iconId}.webp"); - bookmark.FaviconPath = faviconPath; + if (savedPaths.TryAdd(faviconPath, true)) + { + FaviconHelper.SaveBitmapData(webpData, faviconPath); + } + + bookmark.FaviconPath = faviconPath; + } } catch (Exception ex) { 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 078476242..7d3cf164c 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj @@ -45,12 +45,14 @@ $(OutputPath)runtimes\linux-musl-arm; $(OutputPath)runtimes\linux-musl-arm64; $(OutputPath)runtimes\linux-musl-x64; + $(OutputPath)runtimes\linux-musl-s390x; $(OutputPath)runtimes\linux-ppc64le; $(OutputPath)runtimes\linux-s390x; $(OutputPath)runtimes\linux-x64; $(OutputPath)runtimes\linux-x86; $(OutputPath)runtimes\maccatalyst-arm64; $(OutputPath)runtimes\maccatalyst-x64; + $(OutputPath)runtimes\osx; $(OutputPath)runtimes\osx-arm64; $(OutputPath)runtimes\osx-x64"/> @@ -64,12 +66,14 @@ $(PublishDir)runtimes\linux-musl-arm; $(PublishDir)runtimes\linux-musl-arm64; $(PublishDir)runtimes\linux-musl-x64; + $(PublishDir)runtimes\linux-musl-s390x; $(PublishDir)runtimes\linux-ppc64le; $(PublishDir)runtimes\linux-s390x; $(PublishDir)runtimes\linux-x64; $(PublishDir)runtimes\linux-x86; $(PublishDir)runtimes\maccatalyst-arm64; $(PublishDir)runtimes\maccatalyst-x64; + $(PublishDir)runtimes\osx; $(PublishDir)runtimes\osx-arm64; $(PublishDir)runtimes\osx-x64"/> @@ -96,7 +100,9 @@ - + + + diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Helper/FaviconHelper.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Helper/FaviconHelper.cs index b88bd7640..7f17dc6bf 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Helper/FaviconHelper.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Helper/FaviconHelper.cs @@ -1,5 +1,7 @@ using System; using System.IO; +using SkiaSharp; +using Svg.Skia; namespace Flow.Launcher.Plugin.BrowserBookmark.Helper; @@ -65,12 +67,58 @@ public static class FaviconHelper } } - public static bool IsSvgData(byte[] data) + public static byte[] TryConvertToWebp(byte[] data) { - if (data.Length < 5) - return false; - string start = System.Text.Encoding.ASCII.GetString(data, 0, Math.Min(100, data.Length)); - return start.Contains(" - آلة حاسبة - تمكنك من إجراء العمليات الحسابية. (جرب 5*3-2 في Flow Launcher) + آلة حاسبة + تمكنك من إجراء العمليات الحسابية. (جرب 5*3-2 في Flow Launcher) ليست رقمًا (NaN) التعبير خاطئ أو غير مكتمل (هل نسيت بعض الأقواس؟) نسخ هذا الرقم إلى الحافظة diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/cs.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/cs.xaml index 0767248b7..56b908031 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/cs.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/cs.xaml @@ -1,8 +1,8 @@  - Kalkulačka - Umožňuje provádět matematické výpočty.(Try 5*3-2 v průtokovém spouštěči) + Kalkulačka + Umožňuje provádět matematické výpočty.(Try 5*3-2 v průtokovém spouštěči) Není číslo (NaN) Nesprávný nebo neúplný výraz (Nezapomněli jste na závorky?) Kopírování výsledku do schránky diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/da.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/da.xaml index 8287fe2f1..c5690a3d5 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/da.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/da.xaml @@ -1,8 +1,8 @@  - Calculator - Allows to do mathematical calculations.(Try 5*3-2 in Flow Launcher) + Calculator + Allows to do mathematical calculations.(Try 5*3-2 in Flow Launcher) Not a number (NaN) Expression wrong or incomplete (Did you forget some parentheses?) Copy this number to the clipboard diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/de.xaml index eead8b6ca..9b8654962 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/de.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/de.xaml @@ -1,8 +1,8 @@  - Rechner - Ermöglicht mathematische Berechnungen. (Versuchen Sie 5*3-2 in Flow Launcher) + Rechner + Ermöglicht mathematische Berechnungen. (Versuchen Sie 5*3-2 in Flow Launcher) Nicht eine Zahl (NaN) Ausdruck falsch oder unvollständig (Haben Sie einige Klammern vergessen?) Diese Zahl in die Zwischenablage kopieren diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml index 502c07238..b71e5d8a0 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml @@ -3,8 +3,8 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:system="clr-namespace:System;assembly=mscorlib"> - Calculator - Allows to do mathematical calculations.(Try 5*3-2 in Flow Launcher) + Calculator + Perform mathematical calculations (including hexadecimal values). Use ',' or '.' as thousand separator or decimal place. Not a number (NaN) Expression wrong or incomplete (Did you forget some parentheses?) Copy this number to the clipboard diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es-419.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es-419.xaml index 7d04db7f5..6b69c06a4 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es-419.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es-419.xaml @@ -1,8 +1,8 @@  - Calculadora - Permite hacer cálculos matemáticos. (Pruebe con 5*3-2 en Flow Launcher) + Calculadora + Permite hacer cálculos matemáticos. (Pruebe con 5*3-2 en Flow Launcher) No es un número (NaN) Expresión incorrecta o incompleta (¿Olvidó algún paréntesis?) Copiar este número al portapapeles diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es.xaml index 7f6e230db..3dfdff680 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/es.xaml @@ -1,8 +1,8 @@  - Calculadora - Permite hacer cálculos matemáticos. (Pruebe 5*3-2 en Flow Launcher) + Calculadora + Permite hacer cálculos matemáticos. (Pruebe 5*3-2 en Flow Launcher) No es un número (NaN) Expresión incorrecta o incompleta (¿Ha olvidado algunos paréntesis?) Copiar este número al portapapeles diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/fr.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/fr.xaml index 60087c9a2..a86c020be 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/fr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/fr.xaml @@ -1,8 +1,8 @@  - Calculatrice - Permet de faire des calculs mathématiques.(Essayez 5*3-2 dans Flow Launcher) + Calculatrice + Permet de faire des calculs mathématiques.(Essayez 5*3-2 dans Flow Launcher) Pas un nombre (NaN) Expression incorrecte ou incomplète (avez-vous oublié certaines parenthèses ?) Copier ce chiffre dans le presse-papiers diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/he.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/he.xaml index 6bc43d2db..1d297381c 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/he.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/he.xaml @@ -1,8 +1,8 @@  - מחשבון - מאפשר לבצע חישובים מתמטיים. (נסה 5*3-2 ב-Flow Launcher) + מחשבון + מאפשר לבצע חישובים מתמטיים. (נסה 5*3-2 ב-Flow Launcher) לא מספר (NaN) הביטוי שגוי או לא שלם (האם שכחת סוגריים?) העתק מספר זה ללוח diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/it.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/it.xaml index 343e0feb3..27bd75304 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/it.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/it.xaml @@ -1,8 +1,8 @@  - Calcolatrice - Consente di eseguire calcoli matematici (provare 5*3-2 in Flow Launcher) + Calcolatrice + Consente di eseguire calcoli matematici (provare 5*3-2 in Flow Launcher) Non è un numero (NaN) Espressione sbagliata o incompleta (avete dimenticato delle parentesi?) Copiare questo numero negli appunti diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ja.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ja.xaml index 735f145d9..30bae550a 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ja.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ja.xaml @@ -1,8 +1,8 @@  - 電卓 - 数式の計算ができます(Flow Launcherで「5*3-2」と入力してみてください) + 電卓 + 数式の計算ができます(Flow Launcherで「5*3-2」と入力してみてください) Not a number (NaN) Expression wrong or incomplete (Did you forget some parentheses?) この数字をクリップボードにコピーします diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ko.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ko.xaml index 14e3079f8..30e9b40a0 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ko.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ko.xaml @@ -1,8 +1,8 @@  - 계산기 - 수학 계산을 할 수 있습니다. Flow Launcher에서 5*3-2를 입력해보세요. + 계산기 + 수학 계산을 할 수 있습니다. Flow Launcher에서 5*3-2를 입력해보세요. 숫자가 아님 (NaN) 표현식이 잘못되었거나 불완전합니다. (괄호를 깜빡하셨나요?) 해당 숫자를 클립보드에 복사 diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nb.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nb.xaml index 34a3f9638..be233767f 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nb.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nb.xaml @@ -1,8 +1,8 @@  - Kalkulator - Lar deg gjøre matematiske beregninger. (Prøv 5*3-2 i Flow Launcher) + Kalkulator + Lar deg gjøre matematiske beregninger. (Prøv 5*3-2 i Flow Launcher) Ikke et tall (NaN) Uttrykk feil eller ufullstendig (glem noen parenteser?) Kopier dette nummeret til utklippstavlen diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nl.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nl.xaml index 8287fe2f1..c5690a3d5 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/nl.xaml @@ -1,8 +1,8 @@  - Calculator - Allows to do mathematical calculations.(Try 5*3-2 in Flow Launcher) + Calculator + Allows to do mathematical calculations.(Try 5*3-2 in Flow Launcher) Not a number (NaN) Expression wrong or incomplete (Did you forget some parentheses?) Copy this number to the clipboard diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pl.xaml index 5f6d6e766..75b52685a 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pl.xaml @@ -1,8 +1,8 @@  - Kalkulator - Szybkie wykonywanie obliczeń matematycznych. (Spróbuj wpisać 5*3-2 w oknie Flow Launchera) + Kalkulator + Szybkie wykonywanie obliczeń matematycznych. (Spróbuj wpisać 5*3-2 w oknie Flow Launchera) Nie liczba (NaN) Wyrażenie niepoprawne lub niekompletne (Czy zapomniałeś o nawiasach?) Skopiuj ten numer do schowka diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-br.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-br.xaml index 0eb4f3cd8..9b6f3708a 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-br.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-br.xaml @@ -1,8 +1,8 @@  - Calculadora - Permite fazer cálculos matemáticos.(Tente 5*3-2 no Flow Launcher) + Calculadora + Permite fazer cálculos matemáticos.(Tente 5*3-2 no Flow Launcher) Não é um número (NaN) Expressão errada ou incompleta (Você esqueceu de adicionar parênteses?) Copiar este numero para a área de transferência diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-pt.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-pt.xaml index 937ebb841..7b48d6fe9 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-pt.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-pt.xaml @@ -1,8 +1,8 @@  - Calculadora - Permite a execução de cálculos matemáticos (experimente 5*3-2) + Calculadora + Permite a execução de cálculos matemáticos (experimente 5*3-2) Não é número (NN) Expressão errada ou incompleta (esqueceu-se de algum parêntese?) Copiar número para a área de transferência diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ru.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ru.xaml index 3adb1ea49..d1d03d604 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ru.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/ru.xaml @@ -1,8 +1,8 @@  - Калькулятор - Позволяет выполнять математические вычисления. (Попробуйте 5*3-2 в Flow Launcher) + Калькулятор + Позволяет выполнять математические вычисления. (Попробуйте 5*3-2 в Flow Launcher) Не является числом (NaN) Выражение неправильное или неполное (Вы забыли скобки?) Скопировать этот номер в буфер обмена diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sk.xaml index cf2de9fe5..b1cf4a735 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sk.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sk.xaml @@ -1,8 +1,8 @@  - Kalkulačka - Spracúva matematické operácie. (Skúste 5*3-2 vo Flow Launcheri) + Kalkulačka + Spracúva matematické operácie. (Skúste 5*3-2 vo Flow Launcheri) Nie je číslo (NaN) Nesprávny alebo neúplný výraz (Nezabudli ste na zátvorky?) Kopírovať výsledok do schránky diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sr.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sr.xaml index 8287fe2f1..c5690a3d5 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/sr.xaml @@ -1,8 +1,8 @@  - Calculator - Allows to do mathematical calculations.(Try 5*3-2 in Flow Launcher) + Calculator + Allows to do mathematical calculations.(Try 5*3-2 in Flow Launcher) Not a number (NaN) Expression wrong or incomplete (Did you forget some parentheses?) Copy this number to the clipboard diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/tr.xaml index 9c722bde5..f307c65e2 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/tr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/tr.xaml @@ -1,8 +1,8 @@  - Hesap Makinesi - Matematiksel hesaplamalar yapmaya yarar. (5*3-2 yazmayı deneyin) + Hesap Makinesi + Matematiksel hesaplamalar yapmaya yarar. (5*3-2 yazmayı deneyin) Sayı değil (NaN) İfade hatalı ya da eksik. (Parantez koymayı mı unuttunuz?) Bu sayıyı panoya kopyala diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/uk-UA.xaml index b33993c4a..9f60f570a 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/uk-UA.xaml @@ -1,8 +1,8 @@  - Калькулятор - Дозволяє виконувати математичні обчислення (спробуйте 5*3-2 у Flow Launcher) + Калькулятор + Дозволяє виконувати математичні обчислення (спробуйте 5*3-2 у Flow Launcher) Не є числом (NaN) Вираз неправильний або неповний (Ви забули якісь дужки?) Скопіюйте це число в буфер обміну diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/vi.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/vi.xaml index f03af4ef0..688623b04 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/vi.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/vi.xaml @@ -1,8 +1,8 @@  - Máy tính - Cho phép thực hiện các phép tính toán học. (Thử 5*3-2 trong Flow Launcher) + Máy tính + Cho phép thực hiện các phép tính toán học. (Thử 5*3-2 trong Flow Launcher) Không phải là số (NaN) Biểu thức sai hoặc không đầy đủ (Bạn có quên một số dấu ngoặc đơn không?) Sao chép số này vào clipboard diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-cn.xaml index a0e3d0f92..85159ed4f 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-cn.xaml @@ -1,8 +1,8 @@  - 计算器 - 为 Flow Launcher 提供数学计算能力。(试着在 Flow Launcher 输入 5*3-2) + 计算器 + 为 Flow Launcher 提供数学计算能力。(试着在 Flow Launcher 输入 5*3-2) 请输入数字 表达错误或不完整(您是否忘记了一些括号?) 将结果复制到剪贴板 diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-tw.xaml index 39b8d4e01..d3d448e1c 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/zh-tw.xaml @@ -1,8 +1,8 @@  - 計算機 - 為 Flow Launcher 提供數學計算功能。(試著在 Flow Launcher 輸入 5*3-2) + 計算機 + 為 Flow Launcher 提供數學計算功能。(試著在 Flow Launcher 輸入 5*3-2) 不是一個數 (NaN) Expression wrong or incomplete (Did you forget some parentheses?) 複製此數至剪貼簿 diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs index 195d63e47..6878c54b4 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Windows.Controls; @@ -14,6 +15,9 @@ namespace Flow.Launcher.Plugin.Calculator { private static readonly Regex RegValidExpressChar = MainRegexHelper.GetRegValidExpressChar(); private static readonly Regex RegBrackets = MainRegexHelper.GetRegBrackets(); + private static readonly Regex ThousandGroupRegex = MainRegexHelper.GetThousandGroupRegex(); + private static readonly Regex NumberRegex = MainRegexHelper.GetNumberRegex(); + private static Engine MagesEngine; private const string Comma = ","; private const string Dot = "."; @@ -23,6 +27,16 @@ namespace Flow.Launcher.Plugin.Calculator private Settings _settings; private SettingsViewModel _viewModel; + /// + /// Holds the formatting information for a single query. + /// This is used to ensure thread safety by keeping query state local. + /// + private class ParsingContext + { + public string InputDecimalSeparator { get; set; } + public bool InputUsesGroupSeparators { get; set; } + } + public void Init(PluginInitContext context) { Context = context; @@ -45,20 +59,11 @@ namespace Flow.Launcher.Plugin.Calculator return new List(); } + var context = new ParsingContext(); + try { - string expression; - - switch (_settings.DecimalSeparator) - { - case DecimalSeparator.Comma: - case DecimalSeparator.UseSystemLocale when CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator == ",": - expression = query.Search.Replace(",", "."); - break; - default: - expression = query.Search; - break; - } + var expression = NumberRegex.Replace(query.Search, m => NormalizeNumber(m.Value, context)); var result = MagesEngine.Interpret(expression); @@ -71,7 +76,7 @@ namespace Flow.Launcher.Plugin.Calculator if (!string.IsNullOrEmpty(result?.ToString())) { decimal roundedResult = Math.Round(Convert.ToDecimal(result), _settings.MaxDecimalPlaces, MidpointRounding.AwayFromZero); - string newResult = ChangeDecimalSeparator(roundedResult, GetDecimalSeparator()); + string newResult = FormatResult(roundedResult, context); return new List { @@ -107,9 +112,137 @@ namespace Flow.Launcher.Plugin.Calculator return new List(); } + /// + /// Parses a string representation of a number, detecting its format. It uses structural analysis + /// and falls back to system culture for truly ambiguous cases (e.g., "1,234"). + /// It populates the provided ParsingContext with the detected format for later use. + /// + /// A normalized number string with '.' as the decimal separator for the Mages engine. + private string NormalizeNumber(string numberStr, ParsingContext context) + { + var systemGroupSep = CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator; + int dotCount = numberStr.Count(f => f == '.'); + int commaCount = numberStr.Count(f => f == ','); + + // Case 1: Unambiguous mixed separators (e.g., "1.234,56") + if (dotCount > 0 && commaCount > 0) + { + context.InputUsesGroupSeparators = true; + if (numberStr.LastIndexOf('.') > numberStr.LastIndexOf(',')) + { + context.InputDecimalSeparator = Dot; + return numberStr.Replace(Comma, string.Empty); + } + else + { + context.InputDecimalSeparator = Comma; + return numberStr.Replace(Dot, string.Empty).Replace(Comma, Dot); + } + } + + // Case 2: Only dots + if (dotCount > 0) + { + if (dotCount > 1) + { + context.InputUsesGroupSeparators = true; + return numberStr.Replace(Dot, string.Empty); + } + // A number is ambiguous if it has a single Dot in the thousands position, + // and does not start with a "0." or "." + bool isAmbiguous = numberStr.Length - numberStr.LastIndexOf('.') == 4 + && !numberStr.StartsWith("0.") + && !numberStr.StartsWith("."); + if (isAmbiguous) + { + if (systemGroupSep == Dot) + { + context.InputUsesGroupSeparators = true; + return numberStr.Replace(Dot, string.Empty); + } + else + { + context.InputDecimalSeparator = Dot; + return numberStr; + } + } + else // Unambiguous decimal (e.g., "12.34" or "0.123" or ".123") + { + context.InputDecimalSeparator = Dot; + return numberStr; + } + } + + // Case 3: Only commas + if (commaCount > 0) + { + if (commaCount > 1) + { + context.InputUsesGroupSeparators = true; + return numberStr.Replace(Comma, string.Empty); + } + // A number is ambiguous if it has a single Comma in the thousands position, + // and does not start with a "0," or "," + bool isAmbiguous = numberStr.Length - numberStr.LastIndexOf(',') == 4 + && !numberStr.StartsWith("0,") + && !numberStr.StartsWith(","); + if (isAmbiguous) + { + if (systemGroupSep == Comma) + { + context.InputUsesGroupSeparators = true; + return numberStr.Replace(Comma, string.Empty); + } + else + { + context.InputDecimalSeparator = Comma; + return numberStr.Replace(Comma, Dot); + } + } + else // Unambiguous decimal (e.g., "12,34" or "0,123" or ",123") + { + context.InputDecimalSeparator = Comma; + return numberStr.Replace(Comma, Dot); + } + } + + // Case 4: No separators + return numberStr; + } + + private string FormatResult(decimal roundedResult, ParsingContext context) + { + string decimalSeparator = context.InputDecimalSeparator ?? GetDecimalSeparator(); + string groupSeparator = GetGroupSeparator(decimalSeparator); + + string resultStr = roundedResult.ToString(CultureInfo.InvariantCulture); + + string[] parts = resultStr.Split('.'); + string integerPart = parts[0]; + string fractionalPart = parts.Length > 1 ? parts[1] : string.Empty; + + if (context.InputUsesGroupSeparators && integerPart.Length > 3) + { + integerPart = ThousandGroupRegex.Replace(integerPart, groupSeparator); + } + + if (!string.IsNullOrEmpty(fractionalPart)) + { + return integerPart + decimalSeparator + fractionalPart; + } + + return integerPart; + } + + private string GetGroupSeparator(string decimalSeparator) + { + // This logic is now independent of the system's group separator + // to ensure consistent output for unit testing. + return decimalSeparator == Dot ? Comma : Dot; + } + private bool CanCalculate(Query query) { - // Don't execute when user only input "e" or "i" keyword if (query.Search.Length < 2) { return false; @@ -125,28 +258,10 @@ namespace Flow.Launcher.Plugin.Calculator return false; } - if ((query.Search.Contains(Dot) && GetDecimalSeparator() != Dot) || - (query.Search.Contains(Comma) && GetDecimalSeparator() != Comma)) - return false; - return true; } - private static string ChangeDecimalSeparator(decimal value, string newDecimalSeparator) - { - if (string.IsNullOrEmpty(newDecimalSeparator)) - { - return value.ToString(); - } - - var numberFormatInfo = new NumberFormatInfo - { - NumberDecimalSeparator = newDecimalSeparator - }; - return value.ToString(numberFormatInfo); - } - - private string GetDecimalSeparator() + private string GetDecimalSeparator() { string systemDecimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; return _settings.DecimalSeparator switch @@ -179,12 +294,12 @@ namespace Flow.Launcher.Plugin.Calculator public string GetTranslatedPluginTitle() { - return Localize.flowlauncher_plugin_caculator_plugin_name(); + return Localize.flowlauncher_plugin_calculator_plugin_name(); } public string GetTranslatedPluginDescription() { - return Localize.flowlauncher_plugin_caculator_plugin_description(); + return Localize.flowlauncher_plugin_calculator_plugin_description(); } public Control CreateSettingPanel() diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs b/Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs index 8ffc547d1..f4e2090e7 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs +++ b/Plugins/Flow.Launcher.Plugin.Calculator/MainRegexHelper.cs @@ -10,4 +10,10 @@ internal static partial class MainRegexHelper [GeneratedRegex(@"^(ceil|floor|exp|pi|e|max|min|det|abs|log|ln|sqrt|sin|cos|tan|arcsin|arccos|arctan|eigval|eigvec|eig|sum|polar|plot|round|sort|real|zeta|bin2dec|hex2dec|oct2dec|factorial|sign|isprime|isinfty|==|~=|&&|\|\||(?:\<|\>)=?|[ei]|[0-9]|0x[\da-fA-F]+|[\+\%\-\*\/\^\., ""]|[\(\)\|\!\[\]])+$", RegexOptions.Compiled)] public static partial Regex GetRegValidExpressChar(); + + [GeneratedRegex(@"[\d\.,]+", RegexOptions.Compiled)] + public static partial Regex GetNumberRegex(); + + [GeneratedRegex(@"\B(?=(\d{3})+(?!\d))", RegexOptions.Compiled)] + public static partial Regex GetThousandGroupRegex(); } diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/NumberTranslator.cs b/Plugins/Flow.Launcher.Plugin.Calculator/NumberTranslator.cs deleted file mode 100644 index 4eacb9d34..000000000 --- a/Plugins/Flow.Launcher.Plugin.Calculator/NumberTranslator.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System.Globalization; -using System.Text; -using System.Text.RegularExpressions; - -namespace Flow.Launcher.Plugin.Calculator -{ - /// - /// Tries to convert all numbers in a text from one culture format to another. - /// - public class NumberTranslator - { - private readonly CultureInfo sourceCulture; - private readonly CultureInfo targetCulture; - private readonly Regex splitRegexForSource; - private readonly Regex splitRegexForTarget; - - private NumberTranslator(CultureInfo sourceCulture, CultureInfo targetCulture) - { - this.sourceCulture = sourceCulture; - this.targetCulture = targetCulture; - - this.splitRegexForSource = GetSplitRegex(this.sourceCulture); - this.splitRegexForTarget = GetSplitRegex(this.targetCulture); - } - - /// - /// Create a new - returns null if no number conversion - /// is required between the cultures. - /// - /// source culture - /// target culture - /// - public static NumberTranslator Create(CultureInfo sourceCulture, CultureInfo targetCulture) - { - bool conversionRequired = sourceCulture.NumberFormat.NumberDecimalSeparator != targetCulture.NumberFormat.NumberDecimalSeparator - || sourceCulture.NumberFormat.PercentGroupSeparator != targetCulture.NumberFormat.PercentGroupSeparator - || sourceCulture.NumberFormat.NumberGroupSizes != targetCulture.NumberFormat.NumberGroupSizes; - return conversionRequired - ? new NumberTranslator(sourceCulture, targetCulture) - : null; - } - - /// - /// Translate from source to target culture. - /// - /// - /// - public string Translate(string input) - { - return this.Translate(input, this.sourceCulture, this.targetCulture, this.splitRegexForSource); - } - - /// - /// Translate from target to source culture. - /// - /// - /// - public string TranslateBack(string input) - { - return this.Translate(input, this.targetCulture, this.sourceCulture, this.splitRegexForTarget); - } - - private string Translate(string input, CultureInfo cultureFrom, CultureInfo cultureTo, Regex splitRegex) - { - var outputBuilder = new StringBuilder(); - - string[] tokens = splitRegex.Split(input); - foreach (string token in tokens) - { - decimal number; - outputBuilder.Append( - decimal.TryParse(token, NumberStyles.Number, cultureFrom, out number) - ? number.ToString(cultureTo) - : token); - } - - return outputBuilder.ToString(); - } - - private Regex GetSplitRegex(CultureInfo culture) - { - var splitPattern = $"((?:\\d|{Regex.Escape(culture.NumberFormat.NumberDecimalSeparator)}"; - if (!string.IsNullOrEmpty(culture.NumberFormat.NumberGroupSeparator)) - { - splitPattern += $"|{Regex.Escape(culture.NumberFormat.NumberGroupSeparator)}"; - } - splitPattern += ")+)"; - return new Regex(splitPattern); - } - } -} diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml.cs index 77cb4627d..7bc307d11 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml.cs @@ -1,5 +1,4 @@ -using System.Windows; -using System.Windows.Controls; +using System.Windows.Controls; using Flow.Launcher.Plugin.Calculator.ViewModels; namespace Flow.Launcher.Plugin.Calculator.Views diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json b/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json index 3168edfcc..c9435e043 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json @@ -2,8 +2,8 @@ "ID": "CEA0FDFC6D3B4085823D60DC76F28855", "ActionKeyword": "*", "Name": "Calculator", - "Description": "Perform mathematical calculations (including hexadecimal values)", - "Author": "cxfksword", + "Description": "Perform mathematical calculations (including hexadecimal values). Use ',' or '.' as thousand separator or decimal place.", + "Author": "cxfksword, dcog989", "Version": "1.0.0", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/PreviewPanel.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Views/PreviewPanel.xaml index 0daa36e63..4479571e3 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/PreviewPanel.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/PreviewPanel.xaml @@ -7,6 +7,7 @@ xmlns:sys="clr-namespace:System;assembly=System.Runtime" d:DesignHeight="300" d:DesignWidth="300" + DataContext="{Binding RelativeSource={RelativeSource Self}}" mc:Ignorable="d"> @@ -20,40 +21,22 @@ - - - - + Source="{Binding PreviewImage, IsAsync=True, Mode=OneWay}" /> - - - + Text="{Binding FilePath, Mode=OneTime}" /> - - - + + + @@ -87,7 +70,7 @@ - + @@ -105,7 +88,7 @@ Style="{DynamicResource PreviewItemSubTitleStyle}" Text="{DynamicResource FileSize}" TextWrapping="Wrap" - Visibility="{Binding FileSizeVisibility, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}" /> + Visibility="{Binding FileSizeVisibility, Mode=OneTime}" /> + Visibility="{Binding FileSizeVisibility, Mode=OneTime}" /> + Visibility="{Binding CreatedAtVisibility, Mode=OneTime}" /> + Visibility="{Binding CreatedAtVisibility, Mode=OneTime}" /> + Visibility="{Binding LastModifiedAtVisibility, Mode=OneTime}" /> + Visibility="{Binding LastModifiedAtVisibility, Mode=OneTime}" /> diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/PreviewPanel.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/PreviewPanel.xaml.cs index 6e3cf8466..4dd0588ee 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/PreviewPanel.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/PreviewPanel.xaml.cs @@ -21,22 +21,22 @@ public partial class PreviewPanel : UserControl { private static readonly string ClassName = nameof(PreviewPanel); - private string FilePath { get; } - public string FileSize { get; private set; } = Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); - public string CreatedAt { get; } = ""; - public string LastModifiedAt { get; } = ""; - private ImageSource _previewImage = new BitmapImage(); - private Settings Settings { get; } + public string FilePath { get; } + public string FileName { get; } - public ImageSource PreviewImage - { - get => _previewImage; - private set - { - _previewImage = value; - OnPropertyChanged(); - } - } + [ObservableProperty] + private string _fileSize = Main.Context.API.GetTranslation("plugin_explorer_plugin_tooltip_more_info_unknown"); + + [ObservableProperty] + private string _createdAt = ""; + + [ObservableProperty] + private string _lastModifiedAt = ""; + + [ObservableProperty] + private ImageSource _previewImage = new BitmapImage(); + + private Settings Settings { get; } public Visibility FileSizeVisibility => Settings.ShowFileSizeInPreviewPanel ? Visibility.Visible @@ -57,11 +57,11 @@ public partial class PreviewPanel : UserControl public PreviewPanel(Settings settings, string filePath, ResultType type) { - InitializeComponent(); - Settings = settings; - FilePath = filePath; + FileName = Path.GetFileName(filePath); + + InitializeComponent(); if (Settings.ShowFileSizeInPreviewPanel) { diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/en.xaml index 21f836bec..c6a74a047 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Languages/en.xaml @@ -13,6 +13,7 @@ Edit Add Enabled + Private Mode Enabled Disabled Confirm diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs index 0040cffa7..97f90b52c 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs @@ -71,7 +71,7 @@ namespace Flow.Launcher.Plugin.WebSearch Score = score, Action = c => { - _context.API.OpenWebUrl(searchSource.Url.Replace("{q}", Uri.EscapeDataString(keyword))); + _context.API.OpenWebUrl(searchSource.Url.Replace("{q}", Uri.EscapeDataString(keyword)), searchSource.IsPrivateMode); return true; }, @@ -135,7 +135,7 @@ namespace Flow.Launcher.Plugin.WebSearch ActionKeywordAssigned = searchSource.ActionKeyword == SearchSourceGlobalPluginWildCardSign ? string.Empty : searchSource.ActionKeyword, Action = c => { - _context.API.OpenWebUrl(searchSource.Url.Replace("{q}", Uri.EscapeDataString(o))); + _context.API.OpenWebUrl(searchSource.Url.Replace("{q}", Uri.EscapeDataString(o)), searchSource.IsPrivateMode); return true; }, diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSource.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSource.cs index 9eedd29a3..bfd95c242 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSource.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSource.cs @@ -7,6 +7,7 @@ namespace Flow.Launcher.Plugin.WebSearch public class SearchSource : BaseModel { public string Title { get; set; } + public string ActionKeyword { get; set; } [NotNull] @@ -19,21 +20,17 @@ namespace Flow.Launcher.Plugin.WebSearch /// Custom icons are placed in the user data directory /// [JsonIgnore] - public string IconPath - { - get - { - if (CustomIcon) - return Path.Combine(Main.CustomImagesDirectory, Icon); - - return Path.Combine(Main.DefaultImagesDirectory, Icon); - } - } + public string IconPath => CustomIcon + ? Path.Combine(Main.CustomImagesDirectory, Icon) + : Path.Combine(Main.DefaultImagesDirectory, Icon); public string Url { get; set; } [JsonIgnore] public bool Status => Enabled; + + public bool IsPrivateMode { get; set; } + public bool Enabled { get; set; } public SearchSource DeepCopy() @@ -45,8 +42,10 @@ namespace Flow.Launcher.Plugin.WebSearch Url = Url, Icon = Icon, CustomIcon = CustomIcon, + IsPrivateMode = IsPrivateMode, Enabled = Enabled }; + return webSearch; } } diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSourceSetting.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSourceSetting.xaml index 746c9cf84..c6f9b27f3 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSourceSetting.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SearchSourceSetting.xaml @@ -101,6 +101,7 @@ + + Text="{DynamicResource flowlauncher_plugin_websearch_private_mode_label}" /> + + diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs index f0e5ac327..0c0ac4b84 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs @@ -191,7 +191,19 @@ namespace Flow.Launcher.Plugin.WebSearch [JsonIgnore] public SearchSource SelectedSearchSource { get; set; } - public bool EnableSuggestion { get; set; } + private bool enableSuggestion; + public bool EnableSuggestion + { + get => enableSuggestion; + set + { + if (enableSuggestion != value) + { + enableSuggestion = value; + OnPropertyChanged(nameof(EnableSuggestion)); + } + } + } [JsonIgnore] public SuggestionSource[] Suggestions { get; set; } = { @@ -221,9 +233,5 @@ namespace Flow.Launcher.Plugin.WebSearch } } } - - public string BrowserPath { get; set; } - - public bool OpenInNewBrowser { get; set; } = true; } } diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml b/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml index 1ce9b70b4..33461e9e7 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml @@ -11,10 +11,6 @@ mc:Ignorable="d"> - @@ -96,6 +92,20 @@ + + + + + + + @@ -138,7 +148,7 @@ Margin="{StaticResource SettingPanelItemLeftMargin}" VerticalAlignment="Center" FontSize="11" - IsEnabled="{Binding ElementName=EnableSuggestion, Path=IsChecked}" + IsEnabled="{Binding Settings.EnableSuggestion}" ItemsSource="{Binding Settings.Suggestions}" SelectedItem="{Binding Settings.SelectedSuggestion}" /> - \ 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 e53f4ec75..71dc6ece7 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SettingsControl.xaml.cs @@ -94,7 +94,8 @@ namespace Flow.Launcher.Plugin.WebSearch var columnBinding = headerClicked.Column.DisplayMemberBinding as Binding; var sortBy = columnBinding?.Path.Path ?? headerClicked.Column.Header as string; - if(sortBy != null) { + if (sortBy != null) + { Sort(sortBy, direction); if (direction == ListSortDirection.Ascending) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/setting.json b/Plugins/Flow.Launcher.Plugin.WebSearch/setting.json index 533b894b8..a10ea6f33 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/setting.json +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/setting.json @@ -141,6 +141,6 @@ "Enabled": true } ], - "EnableWebSearchSuggestion": false, - "WebSearchSuggestionSource": "Google" + "EnableSuggestion": false, + "Suggestion": "Google" } \ No newline at end of file