diff --git a/Flow.Launcher.Core/Configuration/Portable.cs b/Flow.Launcher.Core/Configuration/Portable.cs index d7c73fb46ac..069154364ab 100644 --- a/Flow.Launcher.Core/Configuration/Portable.cs +++ b/Flow.Launcher.Core/Configuration/Portable.cs @@ -9,11 +9,15 @@ using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin.SharedCommands; using System.Linq; +using CommunityToolkit.Mvvm.DependencyInjection; +using Flow.Launcher.Plugin; namespace Flow.Launcher.Core.Configuration { public class Portable : IPortable { + private readonly IPublicAPI API = Ioc.Default.GetRequiredService(); + /// /// As at Squirrel.Windows version 1.5.2, UpdateManager needs to be disposed after finish /// @@ -40,7 +44,7 @@ public void DisablePortableMode() #endif IndicateDeletion(DataLocation.PortableDataPath); - MessageBoxEx.Show("Flow Launcher needs to restart to finish disabling portable mode, " + + API.ShowMsgBox("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"); UpdateManager.RestartApp(Constant.ApplicationFileName); @@ -64,7 +68,7 @@ public void EnablePortableMode() #endif IndicateDeletion(DataLocation.RoamingDataPath); - MessageBoxEx.Show("Flow Launcher needs to restart to finish enabling portable mode, " + + API.ShowMsgBox("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"); UpdateManager.RestartApp(Constant.ApplicationFileName); @@ -95,13 +99,13 @@ public void RemoveUninstallerEntry() public void MoveUserDataFolder(string fromLocation, string toLocation) { - FilesFolders.CopyAll(fromLocation, toLocation, MessageBoxEx.Show); + FilesFolders.CopyAll(fromLocation, toLocation, (s) => API.ShowMsgBox(s)); VerifyUserDataAfterMove(fromLocation, toLocation); } public void VerifyUserDataAfterMove(string fromLocation, string toLocation) { - FilesFolders.VerifyBothFolderFilesEqual(fromLocation, toLocation, MessageBoxEx.Show); + FilesFolders.VerifyBothFolderFilesEqual(fromLocation, toLocation, (s) => API.ShowMsgBox(s)); } public void CreateShortcuts() @@ -157,13 +161,13 @@ public void PreStartCleanUpAfterPortabilityUpdate() // delete it and prompt the user to pick the portable data location if (File.Exists(roamingDataDeleteFilePath)) { - FilesFolders.RemoveFolderIfExists(roamingDataDir, MessageBoxEx.Show); + FilesFolders.RemoveFolderIfExists(roamingDataDir, (s) => API.ShowMsgBox(s)); - if (MessageBoxEx.Show("Flow Launcher has detected you enabled portable mode, " + + if (API.ShowMsgBox("Flow Launcher has detected you enabled portable mode, " + "would you like to move it to a different location?", string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.Yes) { - FilesFolders.OpenPath(Constant.RootDirectory, MessageBoxEx.Show); + FilesFolders.OpenPath(Constant.RootDirectory, (s) => API.ShowMsgBox(s)); Environment.Exit(0); } @@ -172,9 +176,9 @@ public void PreStartCleanUpAfterPortabilityUpdate() // delete it and notify the user about it. else if (File.Exists(portableDataDeleteFilePath)) { - FilesFolders.RemoveFolderIfExists(portableDataDir, MessageBoxEx.Show); + FilesFolders.RemoveFolderIfExists(portableDataDir, (s) => API.ShowMsgBox(s)); - MessageBoxEx.Show("Flow Launcher has detected you disabled portable mode, " + + API.ShowMsgBox("Flow Launcher has detected you disabled portable mode, " + "the relevant shortcuts and uninstaller entry have been created"); } } @@ -186,7 +190,7 @@ public bool CanUpdatePortability() if (roamingLocationExists && portableLocationExists) { - MessageBoxEx.Show(string.Format("Flow Launcher detected your user data exists both in {0} and " + + API.ShowMsgBox(string.Format("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.", DataLocation.PortableDataPath, DataLocation.RoamingDataPath, Environment.NewLine)); diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs index 6d41e23834e..451df6147fa 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs @@ -8,11 +8,14 @@ using System.Windows; using System.Windows.Forms; using Flow.Launcher.Core.Resource; +using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.Core.ExternalPlugins.Environments { public abstract class AbstractPluginEnvironment { + protected readonly IPublicAPI API = Ioc.Default.GetRequiredService(); + internal abstract string Language { get; } internal abstract string EnvName { get; } @@ -25,7 +28,7 @@ public abstract class AbstractPluginEnvironment internal virtual string FileDialogFilter => string.Empty; - internal abstract string PluginsSettingsFilePath { get; set; } + internal abstract string PluginsSettingsFilePath { get; set; } internal List PluginMetadataList; @@ -57,7 +60,7 @@ internal IEnumerable Setup() EnvName, Environment.NewLine ); - if (MessageBoxEx.Show(noRuntimeMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No) + if (API.ShowMsgBox(noRuntimeMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No) { var msg = string.Format(InternationalizationManager.Instance.GetTranslation("runtimePluginChooseRuntimeExecutable"), EnvName); string selectedFile; @@ -82,7 +85,7 @@ internal IEnumerable Setup() } else { - MessageBoxEx.Show(string.Format(InternationalizationManager.Instance.GetTranslation("runtimePluginUnableToSetExecutablePath"), Language)); + API.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("runtimePluginUnableToSetExecutablePath"), Language)); Log.Error("PluginsLoader", $"Not able to successfully set {EnvName} path, setting's plugin executable path variable is still an empty string.", $"{Language}Environment"); @@ -98,7 +101,7 @@ private void EnsureLatestInstalled(string expectedPath, string currentPath, stri if (expectedPath == currentPath) return; - FilesFolders.RemoveFolderIfExists(installedDirPath, MessageBoxEx.Show); + FilesFolders.RemoveFolderIfExists(installedDirPath, (s) => API.ShowMsgBox(s)); InstallEnvironment(); diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs index 96c29646e8f..607c1906215 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs @@ -28,7 +28,7 @@ internal PythonEnvironment(List pluginMetadataList, PluginsSetti internal override void InstallEnvironment() { - FilesFolders.RemoveFolderIfExists(InstallPath, MessageBoxEx.Show); + FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s)); // 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 diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs index 0d6f109e07f..399f7cc03fd 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs @@ -25,7 +25,7 @@ internal TypeScriptEnvironment(List pluginMetadataList, PluginsS internal override void InstallEnvironment() { - FilesFolders.RemoveFolderIfExists(InstallPath, MessageBoxEx.Show); + FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s)); DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath).Wait(); diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs index 582a4407c45..e8cb72e11d6 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs @@ -25,7 +25,7 @@ internal TypeScriptV2Environment(List pluginMetadataList, Plugin internal override void InstallEnvironment() { - FilesFolders.RemoveFolderIfExists(InstallPath, MessageBoxEx.Show); + FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s)); DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath).Wait(); diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 29f1604c4b1..6e7b5ec6002 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -14,6 +14,7 @@ using Flow.Launcher.Plugin.SharedCommands; using System.Text.Json; using Flow.Launcher.Core.Resource; +using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.Core.Plugin { @@ -28,7 +29,7 @@ public static class PluginManager public static readonly HashSet GlobalPlugins = new(); public static readonly Dictionary NonGlobalPlugins = new(); - public static IPublicAPI API { private set; get; } + public static IPublicAPI API { get; private set; } = Ioc.Default.GetRequiredService(); private static PluginsSettings Settings; private static List _metadatas; @@ -158,9 +159,8 @@ public static void LoadPlugins(PluginsSettings settings) /// Call initialize for all plugins /// /// return the list of failed to init plugins or null for none - public static async Task InitializePluginsAsync(IPublicAPI api) + public static async Task InitializePluginsAsync() { - API = api; var failedPlugins = new ConcurrentQueue(); var InitTasks = AllPlugins.Select(pair => Task.Run(async delegate @@ -204,7 +204,7 @@ public static async Task InitializePluginsAsync(IPublicAPI api) } InternationalizationManager.Instance.AddPluginLanguageDirectories(GetPluginsForInterface()); - InternationalizationManager.Instance.ChangeLanguage(InternationalizationManager.Instance.Settings.Language); + InternationalizationManager.Instance.ChangeLanguage(Ioc.Default.GetRequiredService().Language); if (failedPlugins.Any()) { @@ -519,7 +519,7 @@ internal static void InstallPlugin(UserPlugin plugin, string zipFilePath, bool c var newPluginPath = Path.Combine(installDirectory, folderName); - FilesFolders.CopyAll(pluginFolderPath, newPluginPath, MessageBoxEx.Show); + FilesFolders.CopyAll(pluginFolderPath, newPluginPath, (s) => API.ShowMsgBox(s)); try { diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 7973c66baa7..4827cf69d41 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -4,6 +4,7 @@ using System.Reflection; using System.Threading.Tasks; using System.Windows; +using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Core.ExternalPlugins.Environments; #pragma warning disable IDE0005 using Flow.Launcher.Infrastructure.Logger; @@ -119,7 +120,7 @@ public static IEnumerable DotNetPlugins(List source) _ = Task.Run(() => { - MessageBoxEx.Show($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + + Ioc.Default.GetRequiredService().ShowMsgBox($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + $"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" + $"Please refer to the logs for more information", "", MessageBoxButton.OK, MessageBoxImage.Warning); diff --git a/Flow.Launcher.Core/Resource/Internationalization.cs b/Flow.Launcher.Core/Resource/Internationalization.cs index ef38e8be097..e2a66656a03 100644 --- a/Flow.Launcher.Core/Resource/Internationalization.cs +++ b/Flow.Launcher.Core/Resource/Internationalization.cs @@ -11,22 +11,24 @@ using Flow.Launcher.Plugin; using System.Globalization; using System.Threading.Tasks; +using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.Core.Resource { public class Internationalization { - public Settings Settings { get; set; } private const string Folder = "Languages"; private const string DefaultLanguageCode = "en"; private const string DefaultFile = "en.xaml"; private const string Extension = ".xaml"; + private readonly Settings _settings; private readonly List _languageDirectories = new List(); private readonly List _oldResources = new List(); private readonly string SystemLanguageCode; - public Internationalization() + public Internationalization(Settings settings) { + _settings = settings; AddFlowLauncherLanguageDirectory(); SystemLanguageCode = GetSystemLanguageCodeAtStartup(); } @@ -141,7 +143,7 @@ private void ChangeLanguage(Language language, bool isSystem) CultureInfo.CurrentUICulture = CultureInfo.CurrentCulture; // Raise event after culture is set - Settings.Language = isSystem ? Constant.SystemLanguageCode : language.LanguageCode; + _settings.Language = isSystem ? Constant.SystemLanguageCode : language.LanguageCode; _ = Task.Run(() => { UpdatePluginMetadataTranslations(); @@ -152,7 +154,7 @@ public bool PromptShouldUsePinyin(string languageCodeToSet) { var languageToSet = GetLanguageByLanguageCode(languageCodeToSet); - if (Settings.ShouldUsePinyin) + if (_settings.ShouldUsePinyin) return false; if (languageToSet != AvailableLanguages.Chinese && languageToSet != AvailableLanguages.Chinese_TW) @@ -162,7 +164,7 @@ public bool PromptShouldUsePinyin(string languageCodeToSet) // "Do you want to search with pinyin?" string text = languageToSet == AvailableLanguages.Chinese ? "是否启用拼音搜索?" : "是否啓用拼音搜索?" ; - if (MessageBoxEx.Show(text, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No) + if (Ioc.Default.GetRequiredService().ShowMsgBox(text, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No) return false; return true; diff --git a/Flow.Launcher.Core/Resource/InternationalizationManager.cs b/Flow.Launcher.Core/Resource/InternationalizationManager.cs index 3d87626e6f9..5d718466c4b 100644 --- a/Flow.Launcher.Core/Resource/InternationalizationManager.cs +++ b/Flow.Launcher.Core/Resource/InternationalizationManager.cs @@ -1,26 +1,12 @@ -namespace Flow.Launcher.Core.Resource +using System; +using CommunityToolkit.Mvvm.DependencyInjection; + +namespace Flow.Launcher.Core.Resource { + [Obsolete("InternationalizationManager.Instance is obsolete. Use Ioc.Default.GetRequiredService() instead.")] public static class InternationalizationManager { - private static Internationalization instance; - private static object syncObject = new object(); - public static Internationalization Instance - { - get - { - if (instance == null) - { - lock (syncObject) - { - if (instance == null) - { - instance = new Internationalization(); - } - } - } - return instance; - } - } + => Ioc.Default.GetRequiredService(); } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index 2c2feeae171..4deea1f6648 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -8,10 +8,12 @@ using System.Windows.Markup; using System.Windows.Media; using System.Windows.Media.Effects; +using System.Windows.Shell; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.UserSettings; -using System.Windows.Shell; +using CommunityToolkit.Mvvm.DependencyInjection; +using Flow.Launcher.Plugin; namespace Flow.Launcher.Core.Resource { @@ -23,10 +25,11 @@ public class Theme private const int ShadowExtraMargin = 32; - private readonly List _themeDirectories = new List(); + private readonly IPublicAPI _api; + private readonly Settings _settings; + private readonly List _themeDirectories = new(); private ResourceDictionary _oldResource; private string _oldTheme; - public Settings Settings { get; set; } private const string Folder = Constant.Themes; private const string Extension = ".xaml"; private string DirectoryPath => Path.Combine(Constant.ProgramDirectory, Folder); @@ -36,8 +39,11 @@ public class Theme private double mainWindowWidth; - public Theme() + public Theme(IPublicAPI publicAPI, Settings settings) { + _api = publicAPI; + _settings = settings; + _themeDirectories.Add(DirectoryPath); _themeDirectories.Add(UserDirectoryPath); MakeSureThemeDirectoriesExist(); @@ -88,7 +94,7 @@ public bool ChangeTheme(string theme) // to things like fonts UpdateResourceDictionary(GetResourceDictionary(theme)); - Settings.Theme = theme; + _settings.Theme = theme; //always allow re-loading default theme, in case of failure of switching to a new theme from default theme @@ -99,7 +105,7 @@ public bool ChangeTheme(string theme) BlurEnabled = Win32Helper.IsBlurTheme(); - if (Settings.UseDropShadowEffect && !BlurEnabled) + if (_settings.UseDropShadowEffect && !BlurEnabled) AddDropShadowEffectToCurrentTheme(); Win32Helper.SetBlurForWindow(Application.Current.MainWindow, BlurEnabled); @@ -109,7 +115,7 @@ public bool ChangeTheme(string theme) Log.Error($"|Theme.ChangeTheme|Theme <{theme}> path can't be found"); if (theme != defaultTheme) { - MessageBoxEx.Show(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_path_not_exists"), theme)); + _api.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_path_not_exists"), theme)); ChangeTheme(defaultTheme); } return false; @@ -119,7 +125,7 @@ public bool ChangeTheme(string theme) Log.Error($"|Theme.ChangeTheme|Theme <{theme}> fail to parse"); if (theme != defaultTheme) { - MessageBoxEx.Show(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_parse_error"), theme)); + _api.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_parse_error"), theme)); ChangeTheme(defaultTheme); } return false; @@ -147,7 +153,7 @@ private ResourceDictionary GetThemeResourceDictionary(string theme) return dict; } - private ResourceDictionary CurrentThemeResourceDictionary() => GetThemeResourceDictionary(Settings.Theme); + private ResourceDictionary CurrentThemeResourceDictionary() => GetThemeResourceDictionary(_settings.Theme); public ResourceDictionary GetResourceDictionary(string theme) { @@ -156,10 +162,10 @@ public ResourceDictionary GetResourceDictionary(string theme) if (dict["QueryBoxStyle"] is Style queryBoxStyle && dict["QuerySuggestionBoxStyle"] is Style querySuggestionBoxStyle) { - var fontFamily = new FontFamily(Settings.QueryBoxFont); - var fontStyle = FontHelper.GetFontStyleFromInvariantStringOrNormal(Settings.QueryBoxFontStyle); - var fontWeight = FontHelper.GetFontWeightFromInvariantStringOrNormal(Settings.QueryBoxFontWeight); - var fontStretch = FontHelper.GetFontStretchFromInvariantStringOrNormal(Settings.QueryBoxFontStretch); + var fontFamily = new FontFamily(_settings.QueryBoxFont); + var fontStyle = FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.QueryBoxFontStyle); + var fontWeight = FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.QueryBoxFontWeight); + var fontStretch = FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.QueryBoxFontStretch); queryBoxStyle.Setters.Add(new Setter(TextBox.FontFamilyProperty, fontFamily)); queryBoxStyle.Setters.Add(new Setter(TextBox.FontStyleProperty, fontStyle)); @@ -184,10 +190,10 @@ public ResourceDictionary GetResourceDictionary(string theme) dict["ItemHotkeyStyle"] is Style resultHotkeyItemStyle && dict["ItemHotkeySelectedStyle"] is Style resultHotkeyItemSelectedStyle) { - Setter fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(Settings.ResultFont)); - Setter fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(Settings.ResultFontStyle)); - Setter fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(Settings.ResultFontWeight)); - Setter fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(Settings.ResultFontStretch)); + Setter fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(_settings.ResultFont)); + Setter fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.ResultFontStyle)); + Setter fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.ResultFontWeight)); + Setter fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.ResultFontStretch)); Setter[] setters = { fontFamily, fontStyle, fontWeight, fontStretch }; Array.ForEach( @@ -199,10 +205,10 @@ public ResourceDictionary GetResourceDictionary(string theme) dict["ItemSubTitleStyle"] is Style resultSubItemStyle && dict["ItemSubTitleSelectedStyle"] is Style resultSubItemSelectedStyle) { - Setter fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(Settings.ResultSubFont)); - Setter fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(Settings.ResultSubFontStyle)); - Setter fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(Settings.ResultSubFontWeight)); - Setter fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(Settings.ResultSubFontStretch)); + Setter fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(_settings.ResultSubFont)); + Setter fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.ResultSubFontStyle)); + Setter fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.ResultSubFontWeight)); + Setter fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.ResultSubFontStretch)); Setter[] setters = { fontFamily, fontStyle, fontWeight, fontStretch }; Array.ForEach( @@ -212,7 +218,7 @@ public ResourceDictionary GetResourceDictionary(string theme) /* Ignore Theme Window Width and use setting */ var windowStyle = dict["WindowStyle"] as Style; - var width = Settings.WindowSize; + var width = _settings.WindowSize; windowStyle.Setters.Add(new Setter(Window.WidthProperty, width)); mainWindowWidth = (double)width; return dict; @@ -220,7 +226,7 @@ public ResourceDictionary GetResourceDictionary(string theme) private ResourceDictionary GetCurrentResourceDictionary( ) { - return GetResourceDictionary(Settings.Theme); + return GetResourceDictionary(_settings.Theme); } public List LoadAvailableThemes() diff --git a/Flow.Launcher.Core/Resource/ThemeManager.cs b/Flow.Launcher.Core/Resource/ThemeManager.cs index 71f9acaa58a..3cbe8319ad3 100644 --- a/Flow.Launcher.Core/Resource/ThemeManager.cs +++ b/Flow.Launcher.Core/Resource/ThemeManager.cs @@ -1,26 +1,12 @@ -namespace Flow.Launcher.Core.Resource +using System; +using CommunityToolkit.Mvvm.DependencyInjection; + +namespace Flow.Launcher.Core.Resource { + [Obsolete("ThemeManager.Instance is obsolete. Use Ioc.Default.GetRequiredService() instead.")] public class ThemeManager { - private static Theme instance; - private static object syncObject = new object(); - public static Theme Instance - { - get - { - if (instance == null) - { - lock (syncObject) - { - if (instance == null) - { - instance = new Theme(); - } - } - } - return instance; - } - } + => Ioc.Default.GetRequiredService(); } } diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index b92d8656873..9a77ece3253 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -22,23 +22,26 @@ namespace Flow.Launcher.Core { public class Updater { - public string GitHubRepository { get; } + public string GitHubRepository { get; init; } - public Updater(string gitHubRepository) + private readonly IPublicAPI _api; + + public Updater(IPublicAPI publicAPI, string gitHubRepository) { + _api = publicAPI; GitHubRepository = gitHubRepository; } private SemaphoreSlim UpdateLock { get; } = new SemaphoreSlim(1); - public async Task UpdateAppAsync(IPublicAPI api, bool silentUpdate = true) + public async Task UpdateAppAsync(bool silentUpdate = true) { await UpdateLock.WaitAsync().ConfigureAwait(false); try { if (!silentUpdate) - api.ShowMsg(api.GetTranslation("pleaseWait"), - api.GetTranslation("update_flowlauncher_update_check")); + _api.ShowMsg(_api.GetTranslation("pleaseWait"), + _api.GetTranslation("update_flowlauncher_update_check")); using var updateManager = await GitHubUpdateManagerAsync(GitHubRepository).ConfigureAwait(false); @@ -53,13 +56,13 @@ public async Task UpdateAppAsync(IPublicAPI api, bool silentUpdate = true) if (newReleaseVersion <= currentVersion) { if (!silentUpdate) - MessageBoxEx.Show(api.GetTranslation("update_flowlauncher_already_on_latest")); + _api.ShowMsgBox(_api.GetTranslation("update_flowlauncher_already_on_latest")); return; } if (!silentUpdate) - api.ShowMsg(api.GetTranslation("update_flowlauncher_update_found"), - api.GetTranslation("update_flowlauncher_updating")); + _api.ShowMsg(_api.GetTranslation("update_flowlauncher_update_found"), + _api.GetTranslation("update_flowlauncher_updating")); await updateManager.DownloadReleases(newUpdateInfo.ReleasesToApply).ConfigureAwait(false); @@ -68,9 +71,9 @@ public async Task UpdateAppAsync(IPublicAPI api, bool silentUpdate = true) if (DataLocation.PortableDataLocationInUse()) { var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion.ToString()}\\{DataLocation.PortableFolderName}"; - FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination, MessageBoxEx.Show); - if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination, MessageBoxEx.Show)) - MessageBoxEx.Show(string.Format(api.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"), + FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination, (s) => _api.ShowMsgBox(s)); + if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination, (s) => _api.ShowMsgBox(s))) + _api.ShowMsgBox(string.Format(_api.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"), DataLocation.PortableDataPath, targetDestination)); } @@ -83,7 +86,7 @@ public async Task UpdateAppAsync(IPublicAPI api, bool silentUpdate = true) Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}"); - if (MessageBoxEx.Show(newVersionTips, api.GetTranslation("update_flowlauncher_new_update"), MessageBoxButton.YesNo) == MessageBoxResult.Yes) + if (_api.ShowMsgBox(newVersionTips, _api.GetTranslation("update_flowlauncher_new_update"), MessageBoxButton.YesNo) == MessageBoxResult.Yes) { UpdateManager.RestartApp(Constant.ApplicationFileName); } @@ -96,8 +99,8 @@ public async Task UpdateAppAsync(IPublicAPI api, bool silentUpdate = true) Log.Exception($"|Updater.UpdateApp|Error Occurred", e); if (!silentUpdate) - api.ShowMsg(api.GetTranslation("update_flowlauncher_fail"), - api.GetTranslation("update_flowlauncher_check_connection")); + _api.ShowMsg(_api.GetTranslation("update_flowlauncher_fail"), + _api.GetTranslation("update_flowlauncher_check_connection")); } finally { diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index 1d6ee5c86aa..5d8b264251a 100644 --- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj +++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj @@ -54,6 +54,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index 0b3f2be6557..030aff7cfc0 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -8,6 +8,7 @@ using System; using System.Threading; using Flow.Launcher.Plugin; +using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.Infrastructure.Http { @@ -17,8 +18,6 @@ public static class Http private static HttpClient client = new HttpClient(); - public static IPublicAPI API { get; set; } - static Http() { // need to be added so it would work on a win10 machine @@ -78,7 +77,7 @@ var userName when string.IsNullOrEmpty(userName) => } catch (UriFormatException e) { - API.ShowMsg("Please try again", "Unable to parse Http Proxy"); + Ioc.Default.GetRequiredService().ShowMsg("Please try again", "Unable to parse Http Proxy"); Log.Exception("Flow.Launcher.Infrastructure.Http", "Unable to parse Uri", e); } } diff --git a/Flow.Launcher.Infrastructure/PinyinAlphabet.cs b/Flow.Launcher.Infrastructure/PinyinAlphabet.cs index 7d72359684d..8eaa757bec1 100644 --- a/Flow.Launcher.Infrastructure/PinyinAlphabet.cs +++ b/Flow.Launcher.Infrastructure/PinyinAlphabet.cs @@ -6,6 +6,7 @@ using JetBrains.Annotations; using Flow.Launcher.Infrastructure.UserSettings; using ToolGood.Words.Pinyin; +using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.Infrastructure { @@ -129,7 +130,12 @@ public class PinyinAlphabet : IAlphabet private Settings _settings; - public void Initialize([NotNull] Settings settings) + public PinyinAlphabet() + { + Initialize(Ioc.Default.GetRequiredService()); + } + + private void Initialize([NotNull] Settings settings) { _settings = settings ?? throw new ArgumentNullException(nameof(settings)); } diff --git a/Flow.Launcher.Infrastructure/StringMatcher.cs b/Flow.Launcher.Infrastructure/StringMatcher.cs index bd5dbdda9be..e85c5d6f442 100644 --- a/Flow.Launcher.Infrastructure/StringMatcher.cs +++ b/Flow.Launcher.Infrastructure/StringMatcher.cs @@ -1,28 +1,35 @@ -using Flow.Launcher.Plugin.SharedModels; +using CommunityToolkit.Mvvm.DependencyInjection; +using Flow.Launcher.Plugin.SharedModels; using System; using System.Collections.Generic; using System.Linq; +using Flow.Launcher.Infrastructure.UserSettings; namespace Flow.Launcher.Infrastructure { public class StringMatcher { - private readonly MatchOption _defaultMatchOption = new MatchOption(); + private readonly MatchOption _defaultMatchOption = new(); public SearchPrecisionScore UserSettingSearchPrecision { get; set; } private readonly IAlphabet _alphabet; - public StringMatcher(IAlphabet alphabet = null) + public StringMatcher(IAlphabet alphabet, Settings settings) { _alphabet = alphabet; + UserSettingSearchPrecision = settings.QuerySearchPrecision; } - public static StringMatcher Instance { get; internal set; } + // This is a workaround to allow unit tests to set the instance + public StringMatcher(IAlphabet alphabet) + { + _alphabet = alphabet; + } public static MatchResult FuzzySearch(string query, string stringToCompare) { - return Instance.FuzzyMatch(query, stringToCompare); + return Ioc.Default.GetRequiredService().FuzzyMatch(query, stringToCompare); } public MatchResult FuzzyMatch(string query, string stringToCompare) @@ -241,16 +248,16 @@ private bool IsAcronymCount(string stringToCompare, int compareStringIndex) return false; } - private bool IsAcronymChar(string stringToCompare, int compareStringIndex) + private static bool IsAcronymChar(string stringToCompare, int compareStringIndex) => char.IsUpper(stringToCompare[compareStringIndex]) || compareStringIndex == 0 || // 0 index means char is the start of the compare string, which is an acronym char.IsWhiteSpace(stringToCompare[compareStringIndex - 1]); - private bool IsAcronymNumber(string stringToCompare, int compareStringIndex) + private static bool IsAcronymNumber(string stringToCompare, int compareStringIndex) => stringToCompare[compareStringIndex] >= 0 && stringToCompare[compareStringIndex] <= 9; // To get the index of the closest space which preceeds the first matching index - private int CalculateClosestSpaceIndex(List spaceIndices, int firstMatchIndex) + private static int CalculateClosestSpaceIndex(List spaceIndices, int firstMatchIndex) { var closestSpaceIndex = -1; diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index c412fb32f5a..d3f44530cdb 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -1,10 +1,11 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Drawing; using System.Text.Json.Serialization; using System.Windows; +using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Infrastructure.Hotkey; +using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.SharedModels; using Flow.Launcher.ViewModel; @@ -13,6 +14,24 @@ namespace Flow.Launcher.Infrastructure.UserSettings { public class Settings : BaseModel, IHotkeySettings { + private FlowLauncherJsonStorage _storage; + private StringMatcher _stringMatcher = null; + + public void SetStorage(FlowLauncherJsonStorage storage) + { + _storage = storage; + } + + public void Initialize() + { + _stringMatcher = Ioc.Default.GetRequiredService(); + } + + public void Save() + { + _storage.Save(); + } + private string language = Constant.SystemLanguageCode; private string _theme = Constant.DefaultTheme; public string Hotkey { get; set; } = $"{KeyConstant.Alt} + {KeyConstant.Space}"; @@ -180,7 +199,6 @@ public CustomBrowserViewModel CustomBrowser } }; - /// /// when false Alphabet static service will always return empty results /// @@ -198,8 +216,8 @@ public SearchPrecisionScore QuerySearchPrecision set { _querySearchPrecision = value; - if (StringMatcher.Instance != null) - StringMatcher.Instance.UserSettingSearchPrecision = value; + if (_stringMatcher != null) + _stringMatcher.UserSettingSearchPrecision = value; } } diff --git a/Flow.Launcher.Test/FuzzyMatcherTest.cs b/Flow.Launcher.Test/FuzzyMatcherTest.cs index 9d3b7e70f66..090719642ed 100644 --- a/Flow.Launcher.Test/FuzzyMatcherTest.cs +++ b/Flow.Launcher.Test/FuzzyMatcherTest.cs @@ -22,8 +22,10 @@ public class FuzzyMatcherTest private const string MicrosoftSqlServerManagementStudio = "Microsoft SQL Server Management Studio"; private const string VisualStudioCode = "Visual Studio Code"; - public static List GetSearchStrings() - => new() + private readonly IAlphabet alphabet = null; + + public List GetSearchStrings() + => new List { Chrome, "Choose which programs you want Windows to use for activities like web browsing, editing photos, sending e-mail, and playing music.", @@ -60,7 +62,7 @@ public void MatchTest() }; var results = new List(); - var matcher = new StringMatcher(); + var matcher = new StringMatcher(alphabet); foreach (var str in sources) { results.Add(new Result @@ -82,7 +84,7 @@ public void MatchTest() public void WhenNotAllCharactersFoundInSearchString_ThenShouldReturnZeroScore(string searchString) { var compareString = "Can have rum only in my glass"; - var matcher = new StringMatcher(); + var matcher = new StringMatcher(alphabet); var scoreResult = matcher.FuzzyMatch(searchString, compareString).RawScore; ClassicAssert.True(scoreResult == 0); @@ -98,7 +100,7 @@ public void GivenQueryString_WhenAppliedPrecisionFiltering_ThenShouldReturnGreat string searchTerm) { var results = new List(); - var matcher = new StringMatcher(); + var matcher = new StringMatcher(alphabet); foreach (var str in GetSearchStrings()) { results.Add(new Result @@ -148,7 +150,7 @@ public void WhenGivenQueryString_ThenShouldReturn_TheDesiredScoring( string queryString, string compareString, int expectedScore) { // When, Given - var matcher = new StringMatcher {UserSettingSearchPrecision = SearchPrecisionScore.Regular}; + var matcher = new StringMatcher(alphabet) {UserSettingSearchPrecision = SearchPrecisionScore.Regular}; var rawScore = matcher.FuzzyMatch(queryString, compareString).RawScore; // Should @@ -182,7 +184,7 @@ public void WhenGivenDesiredPrecision_ThenShouldReturn_AllResultsGreaterOrEqual( bool expectedPrecisionResult) { // When - var matcher = new StringMatcher {UserSettingSearchPrecision = expectedPrecisionScore}; + var matcher = new StringMatcher(alphabet) {UserSettingSearchPrecision = expectedPrecisionScore}; // Given var matchResult = matcher.FuzzyMatch(queryString, compareString); @@ -233,7 +235,7 @@ public void WhenGivenQuery_ShouldReturnResults_ContainingAllQuerySubstrings( bool expectedPrecisionResult) { // When - var matcher = new StringMatcher {UserSettingSearchPrecision = expectedPrecisionScore}; + var matcher = new StringMatcher(alphabet) {UserSettingSearchPrecision = expectedPrecisionScore}; // Given var matchResult = matcher.FuzzyMatch(queryString, compareString); @@ -261,7 +263,7 @@ public void WhenGivenAQuery_Scoring_ShouldGiveMoreWeightToStartOfNewWord( string queryString, string compareString1, string compareString2) { // When - var matcher = new StringMatcher {UserSettingSearchPrecision = SearchPrecisionScore.Regular}; + var matcher = new StringMatcher(alphabet) {UserSettingSearchPrecision = SearchPrecisionScore.Regular}; // Given var compareString1Result = matcher.FuzzyMatch(queryString, compareString1); @@ -294,7 +296,7 @@ public void WhenGivenTwoStrings_Scoring_ShouldGiveMoreWeightToTheStringCloserToI string queryString, string compareString1, string compareString2) { // When - var matcher = new StringMatcher { UserSettingSearchPrecision = SearchPrecisionScore.Regular }; + var matcher = new StringMatcher(alphabet) { UserSettingSearchPrecision = SearchPrecisionScore.Regular }; // Given var compareString1Result = matcher.FuzzyMatch(queryString, compareString1); @@ -324,7 +326,7 @@ public void WhenMultipleResults_ExactMatchingResult_ShouldHaveGreatestScore( string secondName, string secondDescription, string secondExecutableName) { // Act - var matcher = new StringMatcher(); + var matcher = new StringMatcher(alphabet); var firstNameMatch = matcher.FuzzyMatch(queryString, firstName).RawScore; var firstDescriptionMatch = matcher.FuzzyMatch(queryString, firstDescription).RawScore; var firstExecutableNameMatch = matcher.FuzzyMatch(queryString, firstExecutableName).RawScore; @@ -359,7 +361,7 @@ public void WhenMultipleResults_ExactMatchingResult_ShouldHaveGreatestScore( public void WhenGivenAnAcronymQuery_ShouldReturnAcronymScore(string queryString, string compareString, int desiredScore) { - var matcher = new StringMatcher(); + var matcher = new StringMatcher(alphabet); var score = matcher.FuzzyMatch(queryString, compareString).Score; ClassicAssert.IsTrue(score == desiredScore, $@"Query: ""{queryString}"" diff --git a/Flow.Launcher/ActionKeywords.xaml.cs b/Flow.Launcher/ActionKeywords.xaml.cs index ba47a4ded22..c3966e61850 100644 --- a/Flow.Launcher/ActionKeywords.xaml.cs +++ b/Flow.Launcher/ActionKeywords.xaml.cs @@ -44,7 +44,7 @@ private void btnDone_OnClick(object sender, RoutedEventArgs _) else { string msg = translater.GetTranslation("newActionKeywordsHasBeenAssigned"); - MessageBoxEx.Show(msg); + App.API.ShowMsgBox(msg); } } } diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 4d1adc6cd51..952ca70c458 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using System.Windows; +using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Core; using Flow.Launcher.Core.Configuration; using Flow.Launcher.Core.ExternalPlugins.Environments; @@ -14,24 +15,52 @@ using Flow.Launcher.Infrastructure.Http; using Flow.Launcher.Infrastructure.Image; using Flow.Launcher.Infrastructure.Logger; +using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.Plugin; using Flow.Launcher.ViewModel; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch; namespace Flow.Launcher { public partial class App : IDisposable, ISingleInstanceApp { - public static PublicAPIInstance API { get; private set; } + public static IPublicAPI API { get; private set; } private const string Unique = "Flow.Launcher_Unique_Application_Mutex"; private static bool _disposed; - private Settings _settings; - private MainViewModel _mainVM; - private SettingWindowViewModel _settingsVM; - private readonly Updater _updater = new Updater(Flow.Launcher.Properties.Settings.Default.GithubRepo); - private readonly Portable _portable = new Portable(); - private readonly PinyinAlphabet _alphabet = new PinyinAlphabet(); - private StringMatcher _stringMatcher; + private readonly Settings _settings; + + public App() + { + // Initialize settings + var storage = new FlowLauncherJsonStorage(); + _settings = storage.Load(); + _settings.SetStorage(storage); + _settings.WMPInstalled = WindowsMediaPlayerHelper.IsWindowsMediaPlayerInstalled(); + + // Configure the dependency injection container + var host = Host.CreateDefaultBuilder() + .UseContentRoot(AppContext.BaseDirectory) + .ConfigureServices(services => services + .AddSingleton(_ => _settings) + .AddSingleton(sp => new Updater(sp.GetRequiredService(), Launcher.Properties.Settings.Default.GithubRepo)) + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + ).Build(); + Ioc.Default.ConfigureServices(host.Services); + + // Initialize the public API and Settings first + API = Ioc.Default.GetRequiredService(); + _settings.Initialize(); + } [STAThread] public static void Main() @@ -50,52 +79,40 @@ private async void OnStartupAsync(object sender, StartupEventArgs e) { await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => { - _portable.PreStartCleanUpAfterPortabilityUpdate(); + Ioc.Default.GetRequiredService().PreStartCleanUpAfterPortabilityUpdate(); - Log.Info( - "|App.OnStartup|Begin Flow Launcher startup ----------------------------------------------------"); + Log.Info("|App.OnStartup|Begin Flow Launcher startup ----------------------------------------------------"); Log.Info($"|App.OnStartup|Runtime info:{ErrorReporting.RuntimeInfo()}"); + RegisterAppDomainExceptions(); RegisterDispatcherUnhandledException(); var imageLoadertask = ImageLoader.InitializeAsync(); - _settingsVM = new SettingWindowViewModel(_updater, _portable); - _settings = _settingsVM.Settings; - _settings.WMPInstalled = WindowsMediaPlayerHelper.IsWindowsMediaPlayerInstalled(); - AbstractPluginEnvironment.PreStartPluginExecutablePathUpdate(_settings); - _alphabet.Initialize(_settings); - _stringMatcher = new StringMatcher(_alphabet); - StringMatcher.Instance = _stringMatcher; - _stringMatcher.UserSettingSearchPrecision = _settings.QuerySearchPrecision; - - InternationalizationManager.Instance.Settings = _settings; + // TODO: Clean InternationalizationManager.Instance and InternationalizationManager.Instance.GetTranslation in future InternationalizationManager.Instance.ChangeLanguage(_settings.Language); PluginManager.LoadPlugins(_settings.PluginSettings); - _mainVM = new MainViewModel(_settings); - - API = new PublicAPIInstance(_settingsVM, _mainVM, _alphabet); - Http.API = API; Http.Proxy = _settings.Proxy; - await PluginManager.InitializePluginsAsync(API); + await PluginManager.InitializePluginsAsync(); await imageLoadertask; - var window = new MainWindow(_settings, _mainVM); + var mainVM = Ioc.Default.GetRequiredService(); + var window = new MainWindow(_settings, mainVM); Log.Info($"|App.OnStartup|Dependencies Info:{ErrorReporting.DependenciesInfo()}"); Current.MainWindow = window; Current.MainWindow.Title = Constant.FlowLauncher; - HotKeyMapper.Initialize(_mainVM); + HotKeyMapper.Initialize(); // main windows needs initialized before theme change because of blur settings - ThemeManager.Instance.Settings = _settings; + // TODO: Clean ThemeManager.Instance in future ThemeManager.Instance.ChangeTheme(_settings.Theme); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); @@ -141,11 +158,11 @@ private void AutoUpdates() { // check update every 5 hours var timer = new PeriodicTimer(TimeSpan.FromHours(5)); - await _updater.UpdateAppAsync(API); + await Ioc.Default.GetRequiredService().UpdateAppAsync(); while (await timer.WaitForNextTickAsync()) // check updates on startup - await _updater.UpdateAppAsync(API); + await Ioc.Default.GetRequiredService().UpdateAppAsync(); } }); } @@ -188,7 +205,7 @@ public void Dispose() public void OnSecondAppStarted() { - _mainVM.Show(); + Ioc.Default.GetRequiredService().Show(); } } } diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs index 1bd6ee95b83..53fa173a511 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs @@ -6,20 +6,17 @@ using System.Windows; using System.Windows.Input; using System.Windows.Controls; -using Flow.Launcher.Core; namespace Flow.Launcher { public partial class CustomQueryHotkeySetting : Window { - private SettingWindow _settingWidow; private readonly Settings _settings; private bool update; private CustomPluginHotkey updateCustomHotkey; - public CustomQueryHotkeySetting(SettingWindow settingWidow, Settings settings) + public CustomQueryHotkeySetting(Settings settings) { - _settingWidow = settingWidow; _settings = settings; InitializeComponent(); } @@ -63,7 +60,7 @@ public void UpdateItem(CustomPluginHotkey item) o.ActionKeyword == item.ActionKeyword && o.Hotkey == item.Hotkey); if (updateCustomHotkey == null) { - MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("invalidPluginHotkey")); + App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("invalidPluginHotkey")); Close(); return; } diff --git a/Flow.Launcher/CustomShortcutSetting.xaml.cs b/Flow.Launcher/CustomShortcutSetting.xaml.cs index 05d4d3d8396..416f8e04951 100644 --- a/Flow.Launcher/CustomShortcutSetting.xaml.cs +++ b/Flow.Launcher/CustomShortcutSetting.xaml.cs @@ -43,13 +43,13 @@ private void BtnAdd_OnClick(object sender, RoutedEventArgs e) { if (String.IsNullOrEmpty(Key) || String.IsNullOrEmpty(Value)) { - MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("emptyShortcut")); + App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("emptyShortcut")); return; } // Check if key is modified or adding a new one if (((update && originalKey != Key) || !update) && _hotkeyVm.DoesShortcutExist(Key)) { - MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("duplicateShortcut")); + App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("duplicateShortcut")); return; } DialogResult = !update || originalKey != Key || originalValue != Value; diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 2f26017a674..43c1dfd9007 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -1,4 +1,4 @@ - + WinExe @@ -90,6 +90,8 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + + all diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 9e0777a9800..7b2fdfcf499 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -5,10 +5,9 @@ using NHotkey.Wpf; using Flow.Launcher.Core.Resource; using Flow.Launcher.ViewModel; -using Flow.Launcher.Core; using ChefKeys; -using System.Globalization; using Flow.Launcher.Infrastructure.Logger; +using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.Helper; @@ -17,10 +16,10 @@ internal static class HotKeyMapper private static Settings _settings; private static MainViewModel _mainViewModel; - internal static void Initialize(MainViewModel mainVM) + internal static void Initialize() { - _mainViewModel = mainVM; - _settings = _mainViewModel.Settings; + _mainViewModel = Ioc.Default.GetRequiredService(); + _settings = Ioc.Default.GetService(); SetHotkey(_settings.Hotkey, OnToggleHotkey); LoadCustomPluginHotkey(); @@ -85,7 +84,7 @@ internal static void SetHotkey(HotkeyModel hotkey, EventHandler hotkeyStr)); string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr); string errorMsgTitle = InternationalizationManager.Instance.GetTranslation("MessageBoxTitle"); - MessageBoxEx.Show(errorMsg, errorMsgTitle); + App.API.ShowMsgBox(errorMsg, errorMsgTitle); } } diff --git a/Flow.Launcher.Core/MessageBoxEx.xaml b/Flow.Launcher/MessageBoxEx.xaml similarity index 98% rename from Flow.Launcher.Core/MessageBoxEx.xaml rename to Flow.Launcher/MessageBoxEx.xaml index 28cc0228331..a0e5ffaf83a 100644 --- a/Flow.Launcher.Core/MessageBoxEx.xaml +++ b/Flow.Launcher/MessageBoxEx.xaml @@ -1,9 +1,9 @@  _mainVM.VisibilityChanged += value; remove => _mainVM.VisibilityChanged -= value; } - public void CheckForNewUpdate() => _settingsVM.UpdateApp(); + // Must use Ioc.Default.GetRequiredService() to avoid circular dependency + public void CheckForNewUpdate() => _ = Ioc.Default.GetRequiredService().UpdateAppAsync(false); public void SaveAppAllSettings() { PluginManager.Save(); _mainVM.Save(); - _settingsVM.Save(); - ImageLoader.Save(); + _settings.Save(); + _ = ImageLoader.Save(); } public Task ReloadAllPluginData() => PluginManager.ReloadDataAsync(); @@ -105,7 +106,7 @@ public void OpenSettingDialog() { Application.Current.Dispatcher.Invoke(() => { - SettingWindow sw = SingletonWindowOpener.Open(this, _settingsVM); + SettingWindow sw = SingletonWindowOpener.Open(); }); } @@ -247,7 +248,7 @@ public void SavePluginSettings() public void OpenDirectory(string DirectoryPath, string FileNameOrFilePath = null) { using var explorer = new Process(); - var explorerInfo = _settingsVM.Settings.CustomExplorer; + var explorerInfo = _settings.CustomExplorer; explorer.StartInfo = new ProcessStartInfo { @@ -268,7 +269,7 @@ private void OpenUri(Uri uri, bool? inPrivate = null) { if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps) { - var browserInfo = _settingsVM.Settings.CustomBrowser; + var browserInfo = _settings.CustomBrowser; var path = browserInfo.Path == "*" ? "" : browserInfo.Path; diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs index 6e81db5e00d..cb434f399f0 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs @@ -62,7 +62,7 @@ private void OpenWelcomeWindow() [RelayCommand] private void AskClearLogFolderConfirmation() { - var confirmResult = MessageBoxEx.Show( + var confirmResult = App.API.ShowMsgBox( InternationalizationManager.Instance.GetTranslation("clearlogfolderMessage"), InternationalizationManager.Instance.GetTranslation("clearlogfolder"), MessageBoxButton.YesNo @@ -96,7 +96,7 @@ private void OpenLogsFolder() } [RelayCommand] - private Task UpdateApp() => _updater.UpdateAppAsync(App.API, false); + private Task UpdateApp() => _updater.UpdateAppAsync(false); private void ClearLogFolder() { diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs index 3d94355e687..4e498ba23fa 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs @@ -160,7 +160,7 @@ private string GetFileFromDialog(string title, string filter = "") private void UpdateApp() { - _ = _updater.UpdateAppAsync(App.API, false); + _ = _updater.UpdateAppAsync(false); } public bool AutoUpdates diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs index 6d8af9a3f62..b13aaefe3e9 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs @@ -7,7 +7,6 @@ using Flow.Launcher.Infrastructure.Hotkey; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; -using Flow.Launcher.Core; namespace Flow.Launcher.SettingPages.ViewModels; @@ -42,11 +41,11 @@ private void CustomHotkeyDelete() var item = SelectedCustomPluginHotkey; if (item is null) { - MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem")); + App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem")); return; } - var result = MessageBoxEx.Show( + var result = App.API.ShowMsgBox( string.Format( InternationalizationManager.Instance.GetTranslation("deleteCustomHotkeyWarning"), item.Hotkey ), @@ -67,11 +66,11 @@ private void CustomHotkeyEdit() var item = SelectedCustomPluginHotkey; if (item is null) { - MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem")); + App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem")); return; } - var window = new CustomQueryHotkeySetting(null, Settings); + var window = new CustomQueryHotkeySetting(Settings); window.UpdateItem(item); window.ShowDialog(); } @@ -79,7 +78,7 @@ private void CustomHotkeyEdit() [RelayCommand] private void CustomHotkeyAdd() { - new CustomQueryHotkeySetting(null, Settings).ShowDialog(); + new CustomQueryHotkeySetting(Settings).ShowDialog(); } [RelayCommand] @@ -88,11 +87,11 @@ private void CustomShortcutDelete() var item = SelectedCustomShortcut; if (item is null) { - MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem")); + App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem")); return; } - var result = MessageBoxEx.Show( + var result = App.API.ShowMsgBox( string.Format( InternationalizationManager.Instance.GetTranslation("deleteCustomShortcutWarning"), item.Key, item.Value ), @@ -112,7 +111,7 @@ private void CustomShortcutEdit() var item = SelectedCustomShortcut; if (item is null) { - MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem")); + App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem")); return; } diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneProxyViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneProxyViewModel.cs index 1c840fb2703..e2f9e516ca4 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneProxyViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneProxyViewModel.cs @@ -22,7 +22,7 @@ public SettingsPaneProxyViewModel(Settings settings, Updater updater) private void OnTestProxyClicked() { var message = TestProxy(); - MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation(message)); + App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation(message)); } private string TestProxy() diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs index 8d8ccb78041..980b2a811a4 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs @@ -49,7 +49,7 @@ public bool DropShadowEffect { if (ThemeManager.Instance.BlurEnabled && value) { - MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("shadowEffectNotAllowed")); + App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("shadowEffectNotAllowed")); return; } diff --git a/Flow.Launcher/SettingWindow.xaml.cs b/Flow.Launcher/SettingWindow.xaml.cs index d5b30351617..f87194c30cb 100644 --- a/Flow.Launcher/SettingWindow.xaml.cs +++ b/Flow.Launcher/SettingWindow.xaml.cs @@ -3,6 +3,7 @@ using System.Windows.Forms; using System.Windows.Input; using System.Windows.Interop; +using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Core; using Flow.Launcher.Core.Configuration; using Flow.Launcher.Helper; @@ -17,16 +18,21 @@ namespace Flow.Launcher; public partial class SettingWindow { + private readonly Updater _updater; + private readonly IPortable _portable; private readonly IPublicAPI _api; private readonly Settings _settings; private readonly SettingWindowViewModel _viewModel; - public SettingWindow(IPublicAPI api, SettingWindowViewModel viewModel) + public SettingWindow() { - _settings = viewModel.Settings; + var viewModel = Ioc.Default.GetRequiredService(); + _settings = Ioc.Default.GetRequiredService(); DataContext = viewModel; _viewModel = viewModel; - _api = api; + _updater = Ioc.Default.GetRequiredService(); + _portable = Ioc.Default.GetRequiredService(); + _api = Ioc.Default.GetRequiredService(); InitializePosition(); InitializeComponent(); } @@ -125,7 +131,7 @@ public void InitializePosition() WindowState = _settings.SettingWindowState; } - private bool IsPositionValid(double top, double left) + private static bool IsPositionValid(double top, double left) { foreach (var screen in Screen.AllScreens) { @@ -145,7 +151,7 @@ private double WindowLeft() var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position); var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0); var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0); - var left = (dip2.X - this.ActualWidth) / 2 + dip1.X; + var left = (dip2.X - ActualWidth) / 2 + dip1.X; return left; } @@ -154,13 +160,13 @@ private double WindowTop() var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position); var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y); var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Height); - var top = (dip2.Y - this.ActualHeight) / 2 + dip1.Y - 20; + var top = (dip2.Y - ActualHeight) / 2 + dip1.Y - 20; return top; } private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args) { - var paneData = new PaneData(_settings, _viewModel.Updater, _viewModel.Portable); + var paneData = new PaneData(_settings, _updater, _portable); if (args.IsSettingsSelected) { ContentFrame.Navigate(typeof(SettingsPaneGeneral), paneData); diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index dc1fb6f157e..650a276109d 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -25,6 +25,7 @@ using System.ComponentModel; using Flow.Launcher.Infrastructure.Image; using System.Windows.Media; +using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.ViewModel { @@ -56,13 +57,13 @@ public partial class MainViewModel : BaseModel, ISavable #region Constructor - public MainViewModel(Settings settings) + public MainViewModel() { _queryTextBeforeLeaveResults = ""; _queryText = ""; _lastQuery = new Query(); - Settings = settings; + Settings = Ioc.Default.GetRequiredService(); Settings.PropertyChanged += (_, args) => { switch (args.PropertyName) diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs index 04dd6312b37..51afe533d7f 100644 --- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs +++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs @@ -1,66 +1,46 @@ -using Flow.Launcher.Core; -using Flow.Launcher.Core.Configuration; -using Flow.Launcher.Infrastructure.Storage; -using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; namespace Flow.Launcher.ViewModel; -public class SettingWindowViewModel : BaseModel +public partial class SettingWindowViewModel : BaseModel { - private readonly FlowLauncherJsonStorage _storage; + private readonly Settings _settings; - public Updater Updater { get; } - - public IPortable Portable { get; } - - public Settings Settings { get; } - - public SettingWindowViewModel(Updater updater, IPortable portable) + public SettingWindowViewModel(Settings settings) { - _storage = new FlowLauncherJsonStorage(); - - Updater = updater; - Portable = portable; - Settings = _storage.Load(); + _settings = settings; } - public async void UpdateApp() - { - await Updater.UpdateAppAsync(App.API, false); - } - - - /// /// Save Flow settings. Plugins settings are not included. /// public void Save() { - _storage.Save(); + _settings.Save(); } public double SettingWindowWidth { - get => Settings.SettingWindowWidth; - set => Settings.SettingWindowWidth = value; + get => _settings.SettingWindowWidth; + set => _settings.SettingWindowWidth = value; } public double SettingWindowHeight { - get => Settings.SettingWindowHeight; - set => Settings.SettingWindowHeight = value; + get => _settings.SettingWindowHeight; + set => _settings.SettingWindowHeight = value; } public double? SettingWindowTop { - get => Settings.SettingWindowTop; - set => Settings.SettingWindowTop = value; + get => _settings.SettingWindowTop; + set => _settings.SettingWindowTop = value; } public double? SettingWindowLeft { - get => Settings.SettingWindowLeft; - set => Settings.SettingWindowLeft = value; + get => _settings.SettingWindowLeft; + set => _settings.SettingWindowLeft = value; } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs index 34a5b276008..73c35b1c845 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs @@ -31,13 +31,13 @@ public bool KeywordEnabled } private string actionKeyword; - private readonly IPublicAPI api; + private readonly IPublicAPI _api; private bool _keywordEnabled; public ActionKeywordSetting(ActionKeywordModel selectedActionKeyword, IPublicAPI api) { CurrentActionKeyword = selectedActionKeyword; - this.api = api; + _api = api; ActionKeyword = selectedActionKeyword.Keyword; KeywordEnabled = selectedActionKeyword.Enabled; @@ -62,14 +62,14 @@ private void OnDoneButtonClick(object sender, RoutedEventArgs e) switch (CurrentActionKeyword.KeywordProperty, KeywordEnabled) { case (Settings.ActionKeyword.FileContentSearchActionKeyword, true): - api.ShowMsgBox(api.GetTranslation("plugin_explorer_globalActionKeywordInvalid")); + _api.ShowMsgBox(_api.GetTranslation("plugin_explorer_globalActionKeywordInvalid")); return; case (Settings.ActionKeyword.QuickAccessActionKeyword, true): - api.ShowMsgBox(api.GetTranslation("plugin_explorer_quickaccess_globalActionKeywordInvalid")); + _api.ShowMsgBox(_api.GetTranslation("plugin_explorer_quickaccess_globalActionKeywordInvalid")); return; } - if (!KeywordEnabled || !api.ActionKeywordAssigned(ActionKeyword)) + if (!KeywordEnabled || !_api.ActionKeywordAssigned(ActionKeyword)) { DialogResult = true; Close(); @@ -77,7 +77,7 @@ private void OnDoneButtonClick(object sender, RoutedEventArgs e) } // The keyword is not valid, so show message - api.ShowMsgBox(api.GetTranslation("newActionKeywordsHasBeenAssigned")); + _api.ShowMsgBox(_api.GetTranslation("newActionKeywordsHasBeenAssigned")); } private void BtnCancel_OnClick(object sender, RoutedEventArgs e)